2012-12-11 23 views
9

मेरे पास एक विरासत डेटाबेस है, जो एक फाइल के साथ फाइल सिस्टम में नोड्स का प्रतिनिधित्व करता है। कुछ प्रकार के नोड्स हैं उदा। ए, बी, सी और विभिन्न प्रकारों में अलग-अलग गुण होते हैं। वर्तमान डेटाबेस डिज़ाइन में एक तालिका है जिसमें नोड के बारे में जानकारी होती है। यदि नोड टाइप ए का है तो टाइप ए के लिए प्रासंगिक फ़ील्ड सेट हैं। अब मैं मॉडल ए के रूप में ए, बी, सी प्रकारों को व्यक्त करना चाहता हूं। उत्पन्न होने वाली समस्या यह है:Django एकाधिक मॉडल, एक ही तालिका

  1. मुझे ऐसा व्यवहार करना है, सभी तीन प्रकारों का नाम संपत्ति है। मैं नाम प्रॉपर्टी द्वारा फाइल सिस्टम में सभी नोड्स को फ़िल्टर करना चाहता हूं और अच्छे प्रकार की वस्तुओं की एक सूची प्राप्त करना चाहता हूं।

  2. प्रत्येक नोड एक मूल लिंक के रूप में, डेटाबेस में विदेशी कुंजी के रूप में व्यक्त किया गया है, तो शायद कुछ विरासत विरासत होनी चाहिए।

क्या यह django में संभव है?

+0

क्या आप वर्तमान डेटाबेस स्कीमा (विरासत एक) का उपयोग करना चाहते हैं? – borges

उत्तर

3

हां, यह संभव है। यहाँ एक उदाहरण है:

models.py

from django.db import models 

# Create your models here. 
class NodeA(models.Model): 

    name_a = models.CharField(max_length=75, blank=True, null=True) 

    class Meta: 
     db_table = 'Nodes' 
     managed = False 

class NodeB(models.Model): 

    name_b = models.CharField(max_length=75, blank=True, null=True) 

    class Meta: 
     db_table = 'Nodes' 
     managed = False 

class NodeC(models.Model): 

    name_c = models.CharField(max_length=75, blank=True, null=True) 

    class Meta: 
     db_table = 'Nodes' 
     managed = False 

डाटाबेस स्कीमा (SQLite) की अवधारणा की

Nodes { 
    id  integer primary key 
    name_a TEXT 
    name_b TEXT 
    name_c TEXT } 

सबूत

import NodeA, NodeB, NodeC 

a = NodeA() 
a.name_a = 'Node A' 
a.save() 

b = NodeB() 
b.name_b = 'Node B' 
b.save() 

c = NodeC() 
c.name_c = 'Node C' 
c.save() 

यह पैदा करता है:

id  name_a  name_b  name_c 
1   Node A 
2      Node B 
3          Node C 
+0

मैंने इस तरह से इस्तेमाल नहीं किया db_table कभी नहीं देखा है। क्या यह सिंकडब और/या दक्षिण के साथ अच्छी तरह से खेलता है? मैं शायद प्रॉक्सी मॉडल का उपयोग कर कुछ कोशिश की होगी। –

+0

यह सिंकडीबी के साथ अच्छी तरह से काम नहीं करता है। यह उपयोग करने वाले प्रत्येक मॉडल के लिए टेबल बनाने का प्रयास करेगा। इसे मैन्युअल रूप से बनाया जाना चाहिए। और मैं दक्षिण के साथ संगतता से बात नहीं कर सकता। मैं समग्र संगतता से भी बात नहीं कर सकता। मेरे पास इस तरह की स्कीमा कभी नहीं थी, लेकिन मैंने परीक्षण करने के लिए बनाई गई छोटी परीक्षा परियोजना में यह ठीक काम किया। प्रॉक्सी मॉडल शायद मास्टरबोड मॉडल में परिभाषित सभी आवश्यक फ़ील्ड के साथ-साथ प्रॉक्सी मॉडल केवल उन फ़ील्ड का उपयोग करके काम कर सकते थे जो वे चाहते थे। –

+0

प्रबंधित करना = दूसरी और तीसरी तालिका परिभाषाओं के मेटा में झूठा सिंकडीबी के साथ समस्याओं को ठीक करेगा। –

4

मैं कुछ अलग दृष्टिकोण का उपयोग करता हूं, जो एक परिप्रेक्ष्य बनाकर दक्षिण के साथ अच्छी तरह से खेलता है। एक परिप्रेक्ष्य एक प्रॉक्सी है जो मॉडल में कुछ फ़ील्ड का नाम बदलता है, लेकिन कॉलम का नाम रखता है।

मेरे लिए यह django ORM की लचीलापन दिखाने के लिए एक उदाहरण था। मुझे यकीन नहीं है कि क्या आप इसे उत्पादन कोड में उपयोग करना चाहते हैं। इसलिए यह पर्याप्त परीक्षण नहीं किया गया है, लेकिन यह आपको कुछ विचार देगा।

विचार

एक परिप्रेक्ष्य एक मेज, अपने स्वयं के तरीकों जो कर सकते हैं और विभिन्न क्षेत्र के नाम हैं, लेकिन मूल मॉडल और टेबल साझा करने के लिए अलग अलग मॉडल बनाने के उपयोगकर्ता हैं।

यह एक ही तालिका में विभिन्न प्रकारों को स्टोर कर सकता है, जो लॉगिंग या ईवेंट सिस्टम के लिए आसान हो सकता है। प्रत्येक परिप्रेक्ष्य केवल अपनी प्रविष्टियों को देख सकता है, क्योंकि इसे फ़ील्ड नाम action_type पर फ़िल्टर किया गया है।

मॉडल अप्रबंधित हैं, लेकिन एक कस्टम प्रबंधक है, इसलिए दक्षिण इसके लिए नई टेबल नहीं बनाता है।

प्रयोग

कार्यान्वयन एक वर्ग डेकोरेटर, जो Django मॉडल की मेटा डेटा को संशोधित करता है। यह एक "आधार" मॉडल और एलियाज्ड क्षेत्रों का एक शब्दकोश लेता है।

एक उदाहरण पहले देखो दो:

class UserLog(models.Model): 
""" 
A user action log system, user is not in this class, because it clutters import 
""" 
date_created = models.DateTimeField(_("Date created"), auto_now_add=True) 

# Action type is obligatory 

action_type = models.CharField(_("Action Type"), max_length=255) 
integer_field1 = models.IntegerField() 
integer_field2 = models.IntegerField() 
char_field1 = models.CharField(max_length=255) 
char_field2 = models.CharField(max_length=255) 


@ModelPerspective({ 
    'x': 'integer_field1', 
    'y': 'integer_field2', 
    'target': 'char_field1' 
}, UserLog) 
class UserClickLog(models.Model): 
    pass 

यह एक मॉडल है, जो नक्शे integer_field1 को संपत्ति एक्स बनाता है, वाई integer_field2 करने और लक्ष्य char_field1 के लिए और जहां अंतर्निहित तालिका के रूप में तालिका के रूप में एक ही है UserLog।

उपयोग अलग नहीं है तो कोई अन्य मॉडल और दक्षिण केवल उपयोगकर्ता लॉग तालिका बनायेगा।

अब देखते हैं कि इसे कैसे कार्यान्वित किया जाए।

कार्यान्वयन

यह कैसे काम करता है?

यदि कक्षा का मूल्यांकन किया जाता है तो सजावट कक्षा प्राप्त करती है। यह बंदर वर्ग को पैच करेगा, इसलिए यह आपके द्वारा प्रदान की गई आधार तालिका को प्रतिबिंबित करेगा।

अगर हम कोड में कुछ हद तक गहरी चलना उपनाम

जोड़ा जा रहा है। उपनाम शब्दकोश पढ़ा जाता है और प्रत्येक क्षेत्र के लिए बेस फ़ील्ड देखा जाता है। अगर हमें आधार तालिका में फ़ील्ड मिला, तो नाम बदल दिया गया है। इसका एक छोटा दुष्प्रभाव है, यह कॉलम भी बदलता है। तो हमें आधार क्षेत्र से फ़ील्ड कॉलम पुनर्प्राप्त करना होगा। फिर क्षेत्र को contribute_to_class विधि के साथ कक्षा में जोड़ा जाता है, जो सभी बहीखाता का ख्याल रखता है।

तब सभी अलियाकृत गुण मॉडल में जोड़े गए नहीं हैं। यह पर्स की जरूरत नहीं है, लेकिन मैंने उन्हें जोड़ने के लिए चुना है।

गुण

अब हम सभी क्षेत्रों है, हम गुण के एक जोड़े सेट करने के लिए सेट करना। managed संपत्ति तालिका को अनदेखा करने में दक्षिण की ओर चलेगी, लेकिन इसका दुष्प्रभाव है। कक्षा में प्रबंधक नहीं होगा। (हम बाद में इसे ठीक करेंगे)। हम बेस मॉडल से टेबल नाम (db_table) की प्रतिलिपि बनाते हैं और action_type फ़ील्ड को कक्षा के नाम पर डिफ़ॉल्ट बनाते हैं।

आखिरी चीज़ जो हमें करने की ज़रूरत है वह प्रबंधक प्रदान करना है। कुछ देखभाल की जानी चाहिए, क्योंकि डीजेंगो का कहना है कि केवल एक प्रश्नोत्तरी प्रबंधक है। हम प्रबंधक को गहरी प्रतिलिपि से कॉपी करके इसे हल करते हैं और फिर फ़िल्टर विवरण जोड़ते हैं, जो कक्षा के नाम पर फ़िल्टर करता है।

deepcopy (क्वेरीसमूह())। फिल्टर (action_type = cls। वर्गनाम)

यह हमारे तालिका वापसी करते ही प्रासंगिक रिकॉर्ड। अब इसे एक सजावटी में लपेटें और यह हो गया है।

from django.db import models 
from django.db.models.query import QuerySet 

def ModelPerspective(aliases, model): 
    """ 
    This class decorator creates a perspective from a model, which is 
    a proxy with aliased fields. 

    First it will loop over all provided aliases 
    these are pairs of new_field, old_field. 
    Then it will copy the old_fields found in the 
    class to the new fields and change their name, 
    but keep their columnnames. 

    After that it will copy all the fields, which are not aliased. 

Then it will copy all the properties of the model to the new model. 

    Example: 
    @ModelPerspective({ 
     'lusername': 'username', 
     'phonenumber': 'field1' 
    }, User) 
    class Luser(models.Model): 
     pass 

    """ 
    from copy import deepcopy 

    def copy_fields(cls): 

    all_fields = set(map(lambda x: x.name, model._meta.fields)) 
    all_fields.remove('id') 
    # Copy alias fields 

    for alias_field in aliases: 

     real_field = aliases[alias_field] 

     # Get field from model 
     old_field = model._meta.get_field(real_field) 
     oldname, columnname = old_field.get_attname_column() 
     new_field = deepcopy(old_field) 

     # Setting field properties 
     new_field.name = alias_field 
     new_field.db_column = columnname 
     new_field.verbose_name = alias_field 

     new_field.contribute_to_class(cls, "_%s" % alias_field) 
     all_fields.remove(real_field) 

    for field in all_fields: 
     new_field = deepcopy(model._meta.get_field(field)) 
     new_field.contribute_to_class(cls, "_%s" % new_field.name) 

    def copy_properties(cls): 
    # Copy db table 
    cls._meta.db_table = model._meta.db_table 


    def create_manager(cls): 
    from copy import deepcopy 
    field = cls._meta.get_field('action_type') 
    field.default = cls.__name__ 
    # Only query on relevant records 
    qs = deepcopy(cls.objects) 
    cls.objects = qs.filter(action_type=cls.__name__) 

    def wrapper(cls): 

    # Set it unmanaged 
    cls._meta.managed = False 

    copy_properties(cls) 
    copy_fields(cls) 
    create_manager(cls) 

    return cls 
    return wrapper 

इस उत्पादन के लिए तैयार है:

इस कोड है?

मैं इसे उत्पादन कोड में उपयोग नहीं करता, मेरे लिए यह django की लचीलापन दिखाने का एक अभ्यास था, लेकिन पर्याप्त परीक्षण के साथ कोड में इस्तेमाल किया जा सकता है अगर कोई चाहता है।

उत्पादन में उपयोग करने के खिलाफ एक और तर्क यह होगा कि कोड django ORM की उचित कार्यप्रणाली का उचित उपयोग करता है। मुझे यकीन नहीं है कि एपीआई स्थिर होने के लिए पर्याप्त होगा।

और यह समाधान सबसे अच्छा समाधान नहीं है जिसे आप सोच सकते हैं। डेटाबेस में गतिशील फ़ील्ड को संग्रहीत करने की इस समस्या को हल करने के लिए और अधिक संभावनाएं हैं।

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