2016-08-18 69 views
1

我需要在不使用主键的情况下转储和加载模型对象的灯具。模型是平坦的。我知道Django中的自然键,花费了大量时间阅读文档,但所有文档都只有使用自然键而不是关系(fk/m2m)的解决方案。这完全不是我所需要的。如何在Django 1.6模型中使用一些字段而不是主键?

我需要的是这样的:

(models.py)

class Template(models.Model): 
    name = models.CharField(_('name'), max_length=100) 
    content = models.TextField(_('content'), blank=True) 

    def natural_key(self): 
     return (self.name,) 

(fixture1.json)

[ 
{ 
    "pk": null, 
    "model": "dbtemplates.Template", 
    "fields" { 
     "content": "", 
     "name": "product" 
    } 
} 
] 

和命令

./manage.py <SOME_LOADDATA_COMMAND> fixture1.json --natural 

我需要更新我的模板对象,其中h作为名称“产品”或插入它。

标准的Django命令不会这样做。请帮助我解决任何问题。也许有一些这样的图书馆?我很困惑。

Django 1.6。 Python 2.7

回答

3

Django 1.6没有提供使用自然主键转储数据的方法,但Django 1.7确实如此。

不幸的是,use_natural_primary_keys关键字参数不是由基地的Django 1.6串行支持:https://github.com/django/django/blob/1.6.11/django/core/serializers/base.py#L20

所以我建议你升级到django 1.7(我完全理解并不总是可行的),或者你自己编写序列化程序,绘图基于Django 1.7串行器(https://github.com/django/django/blob/1.7.11/django/core/serializers/base.py)的灵感。

+0

非常感谢它解决了我的问题!稍后我将发布移植在Django 1.6下为项目编写的这个功能的源代码。 – zen11625

0

基于answer of régis-b我写了一些代码,允许在Django 1.6“loaddata”管理命令中使用自然键而不升级到1.7。我选择这种方式是因为我的项目完全升级可能会很痛苦。此解决方案可以被视为临时性的。

树结构:

├── project_main_app 
│   ├── __init__.py 
│   ├── backports 
│   │   ├── __init__.py 
│   │   └── django 
│   │    ├── __init__.py 
│   │    └── deserializer.py 
│   └── monkey.py 

project_main_app /反向移植/ django的/ deserializer.py

from __future__ import unicode_literals 

from django.conf import settings 
from django.core.serializers import base 
from django.core.serializers.python import _get_model 
from django.db import models, DEFAULT_DB_ALIAS 
from django.utils.encoding import smart_text 
from django.utils import six 


def Deserializer(object_list, **options): 
    """ 
    Deserialize simple Python objects back into Django ORM instances. 

    It's expected that you pass the Python objects themselves (instead of a 
    stream or a string) to the constructor 
    """ 
    db = options.pop('using', DEFAULT_DB_ALIAS) 
    ignore = options.pop('ignorenonexistent', False) 

    models.get_apps() 
    for d in object_list: 
     # Look up the model and starting build a dict of data for it. 
     Model = _get_model(d["model"]) 
     data = {Model._meta.pk.attname: Model._meta.pk.to_python(d.get("pk", None))} 
     m2m_data = {} 
     model_fields = Model._meta.get_all_field_names() 

     # Handle each field 
     for (field_name, field_value) in six.iteritems(d["fields"]): 

      if ignore and field_name not in model_fields: 
       # skip fields no longer on model 
       continue 

      if isinstance(field_value, str): 
       field_value = smart_text(field_value, options.get("encoding", settings.DEFAULT_CHARSET), strings_only=True) 

      field = Model._meta.get_field(field_name) 

      # Handle M2M relations 
      if field.rel and isinstance(field.rel, models.ManyToManyRel): 
       if hasattr(field.rel.to._default_manager, 'get_by_natural_key'): 
        def m2m_convert(value): 
         if hasattr(value, '__iter__') and not isinstance(value, six.text_type): 
          return field.rel.to._default_manager.db_manager(db).get_by_natural_key(*value).pk 
         else: 
          return smart_text(field.rel.to._meta.pk.to_python(value)) 
       else: 
        m2m_convert = lambda v: smart_text(field.rel.to._meta.pk.to_python(v)) 
       m2m_data[field.name] = [m2m_convert(pk) for pk in field_value] 

      # Handle FK fields 
      elif field.rel and isinstance(field.rel, models.ManyToOneRel): 
       if field_value is not None: 
        if hasattr(field.rel.to._default_manager, 'get_by_natural_key'): 
         if hasattr(field_value, '__iter__') and not isinstance(field_value, six.text_type): 
          obj = field.rel.to._default_manager.db_manager(db).get_by_natural_key(*field_value) 
          value = getattr(obj, field.rel.field_name) 
          # If this is a natural foreign key to an object that 
          # has a FK/O2O as the foreign key, use the FK value 
          if field.rel.to._meta.pk.rel: 
           value = value.pk 
         else: 
          value = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value) 
         data[field.attname] = value 
        else: 
         data[field.attname] = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value) 
       else: 
        data[field.attname] = None 

      # Handle all other fields 
      else: 
       data[field.name] = field.to_python(field_value) 

     # The key block taken from Django 1.7 sources 
     obj = build_instance(Model, data, db) 
     yield base.DeserializedObject(obj, m2m_data) 

# This is also taken from Django 1.7 sources 
def build_instance(Model, data, db): 
    """ 
    Build a model instance. 
    If the model instance doesn't have a primary key and the model supports 
    natural keys, try to retrieve it from the database. 
    """ 
    obj = Model(**data) 
    if (obj.pk is None and hasattr(Model, 'natural_key') and 
      hasattr(Model._default_manager, 'get_by_natural_key')): 
     natural_key = obj.natural_key() 
     try: 
      obj.pk = Model._default_manager.db_manager(db).get_by_natural_key(*natural_key).pk 
     except Model.DoesNotExist: 
      pass 

    return obj 

project_main_app/monkey.py

def patch_all(): 
    import django.core.serializers.python 
    import project_main_app.backports.django.deserializer 
    # Patch the Deserializer 
    django.core.serializers.python.Deserializer = project_main_app.backports.django.deserializer.Deserializer 

project_main_app/init。PY

from project_main_app.monkey import patch_all 
patch_all() 

所以在此之后我只是添加了一些东西,我的模型变得像

class TemplateManager(models.Manager): 
    """1""" 
    def get_by_natural_key(self, name): 
     return self.get(name=name) 


class Template(models.Model): 
    name = models.CharField(_('name'), max_length=100) 
    content = models.TextField(_('content'), blank=True) 
    objects = TemplateManager() # 2   

    def natural_key(self): 
     """3""" 
     return (self.name,) 

,如果灯具有一个空的PK喜欢

[ 
{ 
    "pk": null, 
    "model": "dbtemplates.Template", 
    "fields": { 
     "content": "Some content", 
     "name": "product" 
    } 
} 
] 

标准命令./ manage.py loaddata dbtemplates.Template更新或插入对象匹配名称场。

警告:所有的自然关键组件(如我的例子中的“名称”)必须在数据库中具有唯一值。正确的方法是在定义模型时通过添加参数“unique = True”来设置它们的唯一性。

相关问题