मैं कुछ अलग दृष्टिकोण का उपयोग करता हूं, जो एक परिप्रेक्ष्य बनाकर दक्षिण के साथ अच्छी तरह से खेलता है। एक परिप्रेक्ष्य एक प्रॉक्सी है जो मॉडल में कुछ फ़ील्ड का नाम बदलता है, लेकिन कॉलम का नाम रखता है।
मेरे लिए यह 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 की उचित कार्यप्रणाली का उचित उपयोग करता है। मुझे यकीन नहीं है कि एपीआई स्थिर होने के लिए पर्याप्त होगा।
और यह समाधान सबसे अच्छा समाधान नहीं है जिसे आप सोच सकते हैं। डेटाबेस में गतिशील फ़ील्ड को संग्रहीत करने की इस समस्या को हल करने के लिए और अधिक संभावनाएं हैं।
क्या आप वर्तमान डेटाबेस स्कीमा (विरासत एक) का उपयोग करना चाहते हैं? – borges