2012-05-16 11 views
10

मेरे पास एक Django प्रोजेक्ट है जिसमें एकाधिक django "ऐप्स" हैं। उनमें से एक के पास बाहरी स्रोत से आने वाले डेटा का प्रतिनिधित्व करने के लिए मॉडल हैं (मैं इस डेटा को नियंत्रित नहीं करता)।डीजेगो: डेटाबेस सॉफ्टता के बिना "सॉफ्ट" विदेशी फ़ील्ड चेक

मैं चाहता हूं कि मेरे अन्य ऐप्स इस "बाहरी ऐप" के संदर्भ प्राप्त कर सकें, लेकिन मैं डेटाबेस अखंडता जांच के सभी फ़ज़ से बचना चाहता हूं। मैं नहीं चाहता कि डीबी इन "मुलायम विदेशी कुंजी" पर कोई बाधा डालें।

क्या आप जानते हैं कि मैं एक कस्टम फ़ील्ड को कैसे कोड कर सकता हूं जो डेटाबेस पर हार्ड बाधा उत्पन्न किए बिना असली Django ForeignKey को अनुकरण करेगा?

शायद यह पहले से मौजूद है, लेकिन मेरे पास Google पर कोई भाग्य नहीं था।

:-)

नायब मदद के लिए अग्रिम धन्यवाद: मैं CONTENT_TYPES साथ generic relations प्रणाली के बारे में पता कर रहा हूँ। लेकिन मैं सामान्य संबंध नहीं चाहता हूं। मैं केवल विशिष्ट अखंडता बाधाओं के बिना विशिष्ट संबंधों के लिए विशिष्ट संबंधों को चाहता हूं।

संपादित करें:

मैंने पाया संबंधित लिंक:

लेकिन मैं अपने प्रश्न के लिए एक उचित जवाब नहीं मिला। :(

संपादित करें 2012, जून 4:

मैं क्या किया जाना चाहिए लगता है Django के कोड में गहरी देखा है, लेकिन मुझे लगता है कि बस ForeignKey उपवर्गीकरण पर्याप्त नहीं होगा क्या आप मुझे कुछ दिशा-निर्देश दे सकता है। ? ऐसा करने के तरीके

एनबी पर: मैं अपने डेटाबेस स्कीमा के प्रबंधन के लिए दक्षिण का उपयोग करें, तो मैं समझ मैं भी इस बारे में कुछ करने की आवश्यकता होगी लेकिन यह विषय से बाहर यहाँ हो सकता है :)

+1

यह वास्तव में एक विदेशी कुंजी नहीं है, innit? –

+0

वैसे मैं डीबी बाधा के बिना django ForeignKey की सभी सुविधाओं से लाभ उठाना चाहता हूं। – Robin

+0

उदाहरण के लिए, मैं 'SoftForeignKey' द्वारा संदर्भित तालिका से एक पंक्ति को हटाने के लिए सक्षम होना चाहता हूं, बिना कुंजी को 'कैलड या सेट' किए बिना। और यदि किसी ऑब्जेक्ट में लक्ष्य तालिका में एक गैर-मौजूदा पंक्ति का संदर्भ है, तो उसे 'ऑब्जेक्टडिज़ नॉटएक्सिस्ट' अपवाद उठाना चाहिए। लेकिन मैं डेटाबेस को इस तरह के राज्य को स्वीकार करना चाहता हूं। – Robin

उत्तर

3

यो लोग,

मैं मैं चाहता था बनाने में कामयाब रहे।

पहले, मैं एक नए क्षेत्र बनाया:

from django.db.models.deletion import DO_NOTHING 
from django.db.models.fields.related import ForeignKey, ManyToOneRel 

class SoftForeignKey(ForeignKey): 
    """ 
    This field behaves like a normal django ForeignKey only without hard database constraints. 
    """ 
    def __init__(self, to, to_field=None, rel_class=ManyToOneRel, **kwargs): 
     ForeignKey.__init__(self, to, to_field=to_field, rel_class=rel_class, **kwargs) 
     self.on_delete = DO_NOTHING 

    no_db_constraints = True 

जब से मैं दक्षिण का उपयोग अपने डेटाबेस स्कीमा का प्रबंधन करने, मैं इस जोड़ने के लिए किया था:

from south.modelsinspector import add_introspection_rules 
add_introspection_rules([], [r'^ecm\.lib\.softfk\.SoftForeignKey']) 

फिर, मैं बंदर पड़ा पैच दक्षिण ताकि यह no_db_constraints पैरामीटर खाते में ले जाए।

from django.db.models.deletion import DO_NOTHING 
from django.db.models.fields.related import ForeignKey, ManyToOneRel 
from django.core.management.color import no_style 
from south.db.generic import DatabaseOperations, invalidate_table_constraints, flatten 

def column_sql(self, table_name, field_name, field, tablespace='', with_name=True, field_prepared=False): 
    """ 
    Creates the SQL snippet for a column. Used by add_column and add_table. 
    """ 

    # If the field hasn't already been told its attribute name, do so. 
... 
... 
... 

     if field.rel and self.supports_foreign_keys: 
      # HACK: "soft" FK handling begin 
      if not hasattr(field, 'no_db_constraints') or not field.no_db_constraints: 
       self.add_deferred_sql(
        self.foreign_key_sql(
         table_name, 
         field.column, 
         field.rel.to._meta.db_table, 
         field.rel.to._meta.get_field(field.rel.field_name).column 
        ) 
       ) 
      # HACK: "soft" FK handling end 

    # Things like the contrib.gis module fields have this in 1.1 and below 
    if hasattr(field, 'post_create_sql'): 
     for stmt in field.post_create_sql(no_style(), ta 
.... 
.... 

# monkey patch South here 
DatabaseOperations.column_sql = column_sql 

और::

from django.db.models.deletion import DO_NOTHING 
from django.db.models.fields.related import ForeignKey, ManyToOneRel 
from django.core.management.color import no_style 
from south.db.generic import DatabaseOperations, invalidate_table_constraints, flatten 

@invalidate_table_constraints 
def alter_column(self, table_name, name, field, explicit_name=True, ignore_constraints=False): 
    """ 
    Alters the given column name so it will match the given field. 
    Note that conversion between the two by the database must be possible. 
    Will not automatically add _id by default; to have this behavour, pass 
    explicit_name=False. 

    @param table_name: The name of the table to add the column to 
    @param name: The name of the column to alter 
    @param field: The new field definition to use 
    """ 

    if self.dry_run: 
     if self.debug: 
... 
... 
    if not ignore_constraints: 
     # Add back FK constraints if needed 
     if field.rel and self.supports_foreign_keys: 
      # HACK: "soft" FK handling begin 
      if not hasattr(field, 'no_db_constraints') or not field.no_db_constraints: 
       self.execute(
        self.foreign_key_sql(
         table_name, 
         field.column, 
         field.rel.to._meta.db_table, 
         field.rel.to._meta.get_field(field.rel.field_name).column 
        ) 
       ) 
      # HACK: "soft" FK handling end 

# monkey patch South here 
DatabaseOperations.alter_column = alter_column 

यह वास्तव में बदसूरत है, लेकिन मैं एक और तरीका भी नहीं मिला वहाँ दो कार्यों FK की कमी के निर्माण में शामिल थे।

अब आप सामान्य विदेशीकी की तरह सॉफ़्टफोइनकी फ़ील्ड का उपयोग कर सकते हैं सिवाय इसके कि आपके पास कोई रेफरेंसियल अखंडता प्रवर्तन नहीं होगा।

पूरा बंदर-पैच के लिए यहाँ देखें: http://eve-corp-management.org/projects/ecm/repository/entry/ecm/lib/softfk.py

1

। आप एक अप्रबंधित मॉडल का उपयोग करने का प्रयास कर सकते हैं:

from django.db import models 


class ReferencedModel(models.Model): 
    pass 


class ManagedModel(models.Model): 
    my_fake_fk = models.IntegerField(
     db_column='referenced_model_id' 
    ) 


class UnmanagedModel(models.Model): 
    my_fake_fk = models.ForeignKey(
     ReferencedModel, 
     db_column='referenced_model_id' 
    ) 

    class Meta: 
     managed = False 
     db_table = ManagedModel._meta.db_table 

मॉडल मेटा क्लास में managed=False निर्दिष्ट करना इसके लिए एक डीबी तालिका नहीं बनाएगा। हालांकि, यह अन्य मॉडलों की तरह व्यवहार करेगा।

1

marianobianchi की टिप्पणी के बंद पिगीबैकिंग, ForeignKey.on_delete के लिए विकल्पों में से एक

DO_NOTHING है: कोई कदम उठाएं। यदि आपका डेटाबेस बैकएंड रेफरेंसियल अखंडता को लागू करता है, तो यह एक इंटीग्रिटीइरर का कारण बनता है जब तक कि आप डेटाबेस फ़ील्ड (शायद प्रारंभिक एसक्यूएल का उपयोग करके) पर SQL को मैन्युअल रूप से जोड़ते हैं।

डीबी स्तर पर विदेशी कुंजी बाधाओं को अक्षम करने के साथ यह चाल चलाना चाहिए। मैं जो कह सकता हूं उससे, ऐसा करने के दो तरीके हैं।आप इस तरह पूरी तरह से FK बाधाओं को निष्क्रिय कर सकते हैं:

from django.db.backend.signals import connection_created 
from django.dispatch import receiver 

@receiver(connection_created) 
def disable_constraints(sender, connection): 
    connection.disable_constraint_checking() 

यह Django डाटाबेस बैकेंड तरह लग रहा है, एक constraint_checks_disabled संदर्भ प्रबंधक की पेशकश भी है, तो आप लपेट सकता है प्रासंगिक db इस तरह कोड में पहुँचता भर चेकों को निष्क्रिय करने से बचने के लिए:

from django.db import connection 
with connection.constraint_checks_disabled(): 
    do_stuff() 
2

मैं इज़्ज़ अद-दीन Ruhulessin के सुझाव के लिए कुछ इसी तरह की कोशिश की लेकिन क्योंकि मैं "नकली FK" कॉलम के अलावा अन्य कॉलम यह काम नहीं किया। कोड मैंने कोशिश की थी:

class DynamicPkg(models.Model): 
    @property 
    def cities(self): 
     return City.objects.filter(dpdestinations__dynamic_pkg=self) 


class DynamicPkgDestination(models.Model): 
    dynamic_pkg = models.ForeignKey(DynamicPkg, related_name='destinations') 
    # Indexed because we will be joining City.code to 
    # DynamicPkgDestination.city_code and we want this to be fast. 
    city_code = models.CharField(max_length=10, db_index=True) 


class UnmanagedDynamicPkgDestination(models.Model): 
    dynamic_pkg = models.ForeignKey(DynamicPkg, related_name='destinations') 
    city = models.ForeignKey('City', db_column='city_code', to_field='code', related_name='dpdestinations') 

    class Meta: 
     managed = False 
     db_table = DynamicPkgDestination._meta.db_table 


class City(models.Model): 
    code = models.CharField(max_length=10, unique=True) 

और त्रुटियों मुझे मिल रहे थे:

Error: One or more models did not validate: 
travelbox.dynamicpkgdestination: Accessor for field 'dynamic_pkg' clashes with related field 'DynamicPkg.destinations'. Add a related_name argument to the definition for 'dynamic_pkg'. 
travelbox.dynamicpkgdestination: Reverse query name for field 'dynamic_pkg' clashes with related field 'DynamicPkg.destinations'. Add a related_name argument to the definition for 'dynamic_pkg'. 
travelbox.unmanageddynamicpkgdestination: Accessor for field 'dynamic_pkg' clashes with related field 'DynamicPkg.destinations'. Add a related_name argument to the definition for 'dynamic_pkg'. 
travelbox.unmanageddynamicpkgdestination: Reverse query name for field 'dynamic_pkg' clashes with related field 'DynamicPkg.destinations'. Add a related_name argument to the definition for 'dynamic_pkg'. 

हालांकि मैं एक प्रॉक्सी मॉडल का उपयोग करके एक काम कर समाधान के साथ आने की थी। मैं अभी भी कुछ Django मान्यता है कि प्रॉक्सी मॉडल में शामिल होने से रोकता है क्षेत्रों के आसपास हैक करने की क्या ज़रूरत थी:

class DynamicPkg(models.Model): 
    @property 
    def cities(self): 
     return City.objects.filter(dpdestinations__dynamic_pkg=self) 



def proxify_model(new_class, base): 
    """ 
    Like putting proxy = True in a model's Meta except it doesn't spoil your 
    fun by raising an error if new_class contains model fields. 
    """ 
    new_class._meta.proxy = True 
    # Next 2 lines are what django.db.models.base.ModelBase.__new__ does when 
    # proxy = True (after it has done its spoil-sport validation ;-) 
    new_class._meta.setup_proxy(base) 
    new_class._meta.concrete_model = base._meta.concrete_model 


class DynamicPkgDestination(models.Model): 
    dynamic_pkg = models.ForeignKey(DynamicPkg, related_name='destinations') 
    # Indexed because we will be joining City.code to 
    # DynamicPkgDestination.city_code and we want this to be fast. 
    city_code = city_code_field(db_index=True) 


class ProxyDynamicPkgDestination(DynamicPkgDestination): 
    city = models.ForeignKey('City', db_column='city_code', to_field='code', related_name='dpdestinations') 


proxify_model(ProxyDynamicPkgDestination, DynamicPkgDestination) 


class City(models.Model): 
    code = models.CharField(max_length=10, unique=True) 
+0

आपकी समस्या का उपयोग कर रहे हैं: related_name = 'गंतव्यों' दोनों के लिए विदेशीकी क्रमशः प्रत्येक के लिए प्रबंधित_डिस्टिनेशन और unmanaged_destinations का उपयोग करें। –

3

तुम सिर्फ एक मैदान पर ForeignKey बाधा जांच अक्षम करना चाहते हैं, तो बस उस क्षेत्र के db_constraint=False जोड़ें।

user = models.ForeignKey('User', db_constraint=False) 

यह भी देखें: Django - How to prevent database foreign key constraint creation

0

मैं एक GenericForeignKey का उपयोग करके इस हल:

thing_content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, blank=True, null=True) 
thing_object_id = models.UUIDField(default=uuid.uuid4, blank=True, null=True) 

thing = GenericForeignKey(ct_field='thing_content_type', fk_field='thing_object_id') 

प्लस तरफ, यह बाहर के बॉक्स Django

पर है नकारात्मक पक्ष, आपके मॉडल में तीन अतिरिक्त विशेषताएं हैं।

इसके अतिरिक्त, रिवर्स रिलेशनशिप स्वचालित रूप से काम नहीं करते हैं, लेकिन मेरे मामले में, मैं इसके साथ ठीक हूं।

संबंधित मुद्दे