2016-08-18 38 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」來設置它們的唯一性。

相關問題