জ্যাঙ্গোতে - মডেল উত্তরাধিকার - এটি আপনাকে কোনও পিতামাতার মডেলের বৈশিষ্ট্যকে ওভাররাইড করার অনুমতি দেয়?


102

আমি এটি করতে খুঁজছি:

class Place(models.Model):
   name = models.CharField(max_length=20)
   rating = models.DecimalField()

class LongNamedRestaurant(Place):  # Subclassing `Place`.
   name = models.CharField(max_length=255)  # Notice, I'm overriding `Place.name` to give it a longer length.
   food_type = models.CharField(max_length=25)

এটি আমি যে সংস্করণটি ব্যবহার করতে চাই তা হ'ল (যদিও আমি কোনও পরামর্শের জন্য উন্মুক্ত): http://docs.djangoproject.com/en/dev/topics/db/models/#id7

এটি কি জ্যাঙ্গোতে সমর্থিত? যদি তা না হয় তবে কি একই ধরণের ফলাফল অর্জনের কোনও উপায় আছে?


আপনি দয়া করে নীচে উত্তর গ্রহণ করতে পারেন, জ্যাঙ্গো ১.১০ থেকে এটি সম্ভব :)
হোমস

@ হোলস কেবল যদি বেস ক্লাসটি বিমূর্ত হয়!
মিকাহ ওয়াল্টার

উত্তর:


66

আপডেট উত্তর: লোকেরা মন্তব্যগুলিতে উল্লেখ করেছে, মূল উত্তরটি সঠিকভাবে প্রশ্নের উত্তর দিচ্ছিল না। আসলে, কেবলমাত্র LongNamedRestaurantমডেলটি ডেটাবেজে তৈরি হয়েছিল, Placeছিল না।

একটি সমাধান হ'ল "স্থান" প্রতিনিধিত্বকারী একটি বিমূর্ত মডেল তৈরি করা, যেমন। AbstractPlace, এবং এর উত্তরাধিকারী:

class AbstractPlace(models.Model):
    name = models.CharField(max_length=20)
    rating = models.DecimalField()

    class Meta:
        abstract = True

class Place(AbstractPlace):
    pass

class LongNamedRestaurant(AbstractPlace):
    name = models.CharField(max_length=255)
    food_type = models.CharField(max_length=25)

এছাড়াও দয়া করে @Mark পড়া উত্তর , তিনি একজন মহান ব্যাখ্যা কেন আপনি পরিবর্তন একটি অ-বিমূর্ত ক্লাস থেকে উত্তরাধিকার সুত্রে প্রাপ্ত বৈশিষ্ট্যাবলী করতে পারবে না দেয়।

(দ্রষ্টব্য এটি কেবল জাঙ্গো ১.১০ সাল থেকে সম্ভব: জ্যাঙ্গো ১.১০ এর আগে, বিমূর্ত শ্রেণীর উত্তরাধিকার সূত্রে প্রাপ্ত একটি বৈশিষ্ট্যকে সংশোধন করা সম্ভব ছিল না))

আসল উত্তর

জ্যাঙ্গো ১.১০ থেকে এটি সম্ভব ! আপনি যা চেয়েছিলেন তা করতে হবে:

class Place(models.Model):
    name = models.CharField(max_length=20)
    rating = models.DecimalField()

    class Meta:
        abstract = True

class LongNamedRestaurant(Place):  # Subclassing `Place`.
    name = models.CharField(max_length=255)  # Notice, I'm overriding `Place.name` to give it a longer length.
    food_type = models.CharField(max_length=25)

8
জায়গাটির বিমূর্ত হওয়া দরকার, না?
ডিলানইং

4
আমার মনে হয় না যে আমি অন্য প্রশ্নের উত্তর দিয়েছি কারণ আমি কেবল বলছি যে প্রশ্নটিতে পোস্ট করা কোডটি এখন জাঙ্গো ১.১০ থেকে কাজ করছে। মনে রাখবেন যে তিনি কী ব্যবহার করতে চেয়েছিলেন সে সম্পর্কে তিনি যে লিঙ্কটি পোস্ট করেছিলেন সে অনুসারে তিনি প্লেস ক্লাসটি বিমূর্ত করতে ভুলে গিয়েছেন।
qmarlats

4
নিশ্চিত না কেন এটি গৃহীত উত্তর ... ওপি মাল্টি-টেবিল উত্তরাধিকার ব্যবহার করছে। এই উত্তরটি কেবল বিমূর্ত বেস শ্রেণীর জন্য বৈধ।
মিস্টারনম

4
বিমূর্ত ক্লাসগুলি জ্যাঙ্গো 1.10 এর অনেক আগে পাওয়া গিয়েছিল
rbennell

4
@ নোমজি আমার মূল উত্তরে, Placeবিমূর্ত ছিল, সুতরাং এটি ডেটাবেসে তৈরি হয়নি । কিন্তু ওপি চেয়েছিলেন উভয় Placeএবং LongNamedRestaurantডাটাবেসের মধ্যে তৈরি করা। অতএব আমি AbstractPlaceমডেলটি যুক্ত করতে আমার উত্তর আপডেট করেছি , যা "বেস" (অর্থাত্। বিমূর্ত) মডেল উভয়ই Placeএবং LongNamedRestaurantউত্তরাধিকার সূত্রে প্রাপ্ত। এখন Placeও ও উভয়ই LongNamedRestaurantডাটাবেজে তৈরি করা হয়েছে, যেমন ওপি বলেছে।
Qmarlats

61

না, এটি নয় :

মাঠের নাম "গোপন করা" অনুমোদিত নয়

পাইথন শ্রেণির উত্তরাধিকার সূত্রে, কোনও শিশু শ্রেণীর পক্ষে প্যারেন্ট ক্লাসের কোনও বৈশিষ্ট্য ওভাররাইড করা অনুমোদিত। জ্যাঙ্গোতে, Fieldউদাহরণগুলির মতো বৈশিষ্ট্যগুলির জন্য এটি অনুমোদিত নয় (অন্তত মুহূর্তে নয়)। যদি একটি বেস শ্রেণীর একটি ক্ষেত্র নামে পরিচিত author, আপনি authorসেই বেস শ্রেণীর থেকে উত্তরাধিকার সূত্রে প্রাপ্ত কোনও শ্রেণিতে কল করা অন্য একটি মডেল ক্ষেত্র তৈরি করতে পারবেন না ।


11
কেন এটি অসম্ভব এই জন্য আমার উত্তর দেখুন। লোকেরা এটি পছন্দ করে কারণ এটি বোঝা যায়, এটি অবিলম্বে সুস্পষ্ট নয়।
চিহ্নিত করুন

4
@ লিও-দ্য ম্যানিক আমার মনে হয় User._meta.get_field('email').required = Trueকাজ করতে পারে, নিশ্চিত চিন্তা ছিল না।
জেনস টিমারম্যান

@ লিও-দ্য ম্যানিক, @ জেনস টিম্মারম্যান, @utapyngo আপনার শ্রেণীর সম্পত্তি মান নির্ধারণের উত্তরাধিকার সূত্রে কোনও প্রভাব পড়বে না। আপনাকে _metaপ্যারেন্ট ক্লাসের কাজ MyParentClass._meta.get_field('email').blank = Falseকরতে হবে , উদাহরণস্বরূপ ( emailঅ্যাডমিনে উত্তরাধিকারসূত্রে ক্ষেত্র বাধ্যতামূলক করতে )
পিটারিনো

4
ওফস, দুঃখিত, উপরের ইউটিপাইংগোর কোডটি সঠিক, তবে পরে এটি শ্রেণীর শরীরের বাইরে রাখতে হবে ! আমার পরামর্শ অনুসারে পিতামাত্ত শ্রেণীর ক্ষেত্র নির্ধারণের ফলে অযাচিত পার্শ্ব-প্রতিক্রিয়া থাকতে পারে।
পিটারিনো

আমি অবশ্যই সাব-ক্লাসের প্রতিটি ক্ষেত্রে একটি ক্ষেত্রের চেয়ে আলাদা প্রকারের বিমূর্ত অভিভাবক শ্রেণীর একই নামযুক্ত ক্ষেত্রের চেয়ে আলাদা ক্ষেত্রের গ্যারান্টি রাখতে যাতে সমস্ত সাবক্লাসের একটি নির্দিষ্ট নামের একটি ক্ষেত্র রয়েছে। ইউটাপাইঙ্গোর কোডটি এই প্রয়োজনীয়তাটি পূরণ করে না।
ড্যানিয়েল

28

বিমূর্ত না হওয়া পর্যন্ত এটি সম্ভব নয়, এবং এই কারণেই: LongNamedRestaurantএটি Placeকেবল একটি শ্রেণি হিসাবেই নয়, ডাটাবেসেও রয়েছে। স্থান-সারণীতে প্রতিটি খাঁটি Placeএবং প্রত্যেকের জন্য একটি এন্ট্রি থাকে LongNamedRestaurantLongNamedRestaurantকেবলমাত্র একটি অতিরিক্ত টেবিল তৈরি করে food_typeএবং স্থান টেবিলে একটি রেফারেন্স তৈরি করে।

যদি আপনি এটি করেন তবে আপনি Place.objects.all()যে কোনও এক জায়গা হ'ল এটিও পাবেন LongNamedRestaurantএবং এটি একটি Place( উদাহরণ ছাড়াই food_type) উদাহরণ হবে । সুতরাং Place.nameএবং LongNamedRestaurant.nameএকই ডাটাবেস কলামটি ভাগ করুন, এবং সুতরাং একই ধরণের হতে হবে।

আমি মনে করি এটি সাধারণ মডেলগুলির জন্য অর্থবোধ করে: প্রতিটি রেস্তোঁরা একটি জায়গা এবং সেই জায়গার কমপক্ষে সমস্ত কিছু থাকা উচিত। সম্ভবত এই ধারাবাহিকতাটি কেন এটি 1.10 এর আগে বিমূর্ত মডেলগুলির পক্ষে সম্ভব ছিল না, যদিও এটি সেখানে ডাটাবেস সমস্যা দেয় না। @ ল্যাম্পস্লাভ মন্তব্য হিসাবে, এটি 1.10 এ সম্ভব হয়েছিল। আমি ব্যক্তিগতভাবে যত্নের পরামর্শ দিচ্ছি: যদি সাব.এক্স সুপার.এক্সকে ওভাররাইড করে তবে নিশ্চিত করুন যে সাব.এক্স সুপার.এক্সের একটি সাবক্লাস, অন্যথায় সাব সুপারের জায়গায় সাব ব্যবহার করা যাবে না।

কাজের ক্ষেত্র : আপনি একটি কাস্টম ব্যবহারকারী মডেল তৈরি করতে পারেন ( AUTH_USER_MODEL) যা আপনার যদি কেবল ইমেল ক্ষেত্রটি পরিবর্তন করতে হয় তবে কোড ডুপ্লিকেশনটি বেশ কিছুটা জড়িত। বিকল্পভাবে আপনি ইমেলটি ঠিক সেভাবেই ছেড়ে যেতে পারেন এবং নিশ্চিত করতে পারেন যে এটি সমস্ত ফর্মের মধ্যে প্রয়োজনীয়। এটি যদি ডেটাবেস অখণ্ডতার গ্যারান্টি দেয় না যদি অন্য অ্যাপ্লিকেশনগুলি এটি ব্যবহার করে এবং অন্যান্য উপায়ে কাজ করে না (আপনি যদি ব্যবহারকারীর নাম প্রয়োজন না করতে চান তবে)।


আমি অনুমান করি যে এটি 1.10 এর পরিবর্তনের কারণে: "অ্যাবস্ট্রাক্ট বেস ক্লাসগুলি থেকে উত্তরাধিকার সূত্রে অনুমোদিত ওভাররাইডিং মডেল ক্ষেত্রগুলি।" docs.djangoproject.com/en/2.0/releases/1.10/#models
lampslave

আমি সন্দেহ করি যেহেতু এটি এখনও বের হয়নি, তবে এটি যুক্ত করা ভাল জিনিস, ধন্যবাদ!
চিহ্নিত করুন

19

Https://stackoverflow.com/a/6379556/15690 দেখুন :

class BaseMessage(models.Model):
    is_public = models.BooleanField(default=False)
    # some more fields...

    class Meta:
        abstract = True

class Message(BaseMessage):
    # some fields...
Message._meta.get_field('is_public').default = True

4
অ্যাট্রিবিউটআরার: অ্যাট্রিবিউট সেট করতে পারে না ((((তবে আমি সেট পছন্দ পছন্দ করছি
অ্যালেক্সি

এটি জ্যাঙ্গো 1.11 এ কাজ করে না (এটি পূর্ববর্তী সংস্করণে কাজ করত) ... স্বীকৃত প্রতিক্রিয়া কাজ করে
আকারুচি

9

আপনার কোডটি একটি তাজা অ্যাপ্লিকেশনে আটকানো হয়েছে, INSTALLED_APPS এ অ্যাপ যুক্ত হয়েছে এবং সিঙ্কডিব রান করেছে:

django.core.exceptions.FieldError: Local field 'name' in class 'LongNamedRestaurant' clashes with field of similar name from base class 'Place'

দেখে মনে হচ্ছে জ্যাঙ্গো সেটিকে সমর্থন করে না।


7

কোডের এই সুপারকুলটি আপনাকে বিমূর্ত অভিভাবক শ্রেণিতে ক্ষেত্রগুলিকে 'ওভাররাইড' করতে দেয়।

def AbstractClassWithoutFieldsNamed(cls, *excl):
    """
    Removes unwanted fields from abstract base classes.

    Usage::
    >>> from oscar.apps.address.abstract_models import AbstractBillingAddress

    >>> from koe.meta import AbstractClassWithoutFieldsNamed as without
    >>> class BillingAddress(without(AbstractBillingAddress, 'phone_number')):
    ...     pass
    """
    if cls._meta.abstract:
        remove_fields = [f for f in cls._meta.local_fields if f.name in excl]
        for f in remove_fields:
            cls._meta.local_fields.remove(f)
        return cls
    else:
        raise Exception("Not an abstract model")

ক্ষেত্রগুলি বিমূর্ত অভিভাবক শ্রেণি থেকে সরিয়ে ফেলা হলে আপনি প্রয়োজন হিসাবে সেগুলি পুনরায় সংজ্ঞায়িত করতে মুক্ত হন।

এটি আমার নিজের কাজ নয়। মূল কোডটি এখান থেকে: https://gist.github.com/sp خصوصیundwear / 9d917ddacf3547b646ba


6

হয়ত আপনি অবদান_ থেকে_ক্লাসের সাথে ডিল করতে পারেন:

class LongNamedRestaurant(Place):

    food_type = models.CharField(max_length=25)

    def __init__(self, *args, **kwargs):
        super(LongNamedRestaurant, self).__init__(*args, **kwargs)
        name = models.CharField(max_length=255)
        name.contribute_to_class(self, 'name')

সিঙ্কডিবি ঠিকঠাক কাজ করে। আমি এই উদাহরণটি ব্যবহার করে দেখছি না, আমার ক্ষেত্রে আমি কেবলমাত্র একটি বাধা পরামিতি ওভাররাইড করি তাই ... অপেক্ষা করুন এবং দেখুন!


4
অবদান_ টু-ক্লাসে যুক্তিগুলি অদ্ভুত বলে মনে হচ্ছে (চারপাশেও ভুল উপায়?) মনে হচ্ছে আপনি এটি মেমরি থেকে টাইপ করেছেন। আপনি দয়া করে আপনার পরীক্ষিত প্রকৃত কোড সরবরাহ করতে পারেন? আপনি যদি এই কাজটি করেন তবে আমি কীভাবে এটি করেছি তা জানতে আগ্রহী।
মাইকেল বাইলস্ট্র্রা

এটি আমার পক্ষে কাজ করছে না। পাশাপাশি একটি কাজের উদাহরণে আগ্রহী হবে।
গ্যারামার্ক

দয়া করে দেখুন blog.jupo.org/2011/11/10/django-model-field-injection এটা contribute_to_class হওয়া উচিত (<ModelClass>, <fieldToReplace>)
Goh

4
Place._meta.get_field('name').max_length = 255ক্লাস বডিতে কৌতুক করা উচিত, ওভাররাইড না করে __init__()। আরও সংক্ষিপ্ত হবে।
পিটারিনো

4

আমি জানি এটি একটি পুরানো প্রশ্ন, তবে আমার একইরকম একটি সমস্যা হয়েছিল এবং আমি একটি কার্যকর সমাধান পেয়েছি:

আমার নিম্নলিখিত ক্লাস ছিল:

class CommonInfo(models.Model):
    image = models.ImageField(blank=True, null=True, default="")

    class Meta:
        abstract = True

class Year(CommonInfo):
    year = models.IntegerField() 

তবে আমি চেয়েছিলাম যে সুপারক্লাসের চিত্রের ক্ষেত্রটি নালামযোগ্য রাখার সময় বছরের উত্তরাধিকার সূত্রে প্রাপ্ত চিত্রের ক্ষেত্রটি প্রয়োজন। শেষ পর্যন্ত আমি বৈধতার পর্যায়ে চিত্রটি প্রয়োগ করতে মডেলফর্মগুলি ব্যবহার করেছি:

class YearForm(ModelForm):
    class Meta:
        model = Year

    def clean(self):
        if not self.cleaned_data['image'] or len(self.cleaned_data['image'])==0:
            raise ValidationError("Please provide an image.")

        return self.cleaned_data

প্রশাসক.পি:

class YearAdmin(admin.ModelAdmin):
    form = YearForm

দেখে মনে হচ্ছে এটি কেবল কিছু পরিস্থিতিতে (প্রচ্ছন্ন ক্ষেত্রে আপনাকে কঠোর নিয়মগুলি প্রয়োগ করার দরকার আছে) এর জন্য কেবল এটি প্রযোজ্য।

বিকল্পভাবে আপনি এর clean_<fieldname>()পরিবর্তে পদ্ধতিটি ব্যবহার করতে পারেন clean(), উদাহরণস্বরূপ, যদি কোনও ক্ষেত্রটি townপূরণ করার প্রয়োজন হয়:

def clean_town(self):
    town = self.cleaned_data["town"]
    if not town or len(town) == 0:
        raise forms.ValidationError("Please enter a town")
    return town

1

আপনি মডেল ক্ষেত্রগুলিকে ওভাররাইড করতে পারবেন না, তবে এটি পরিষ্কার () পদ্ধতির ওভাররাইড / নির্দিষ্টকরণের মাধ্যমে সহজেই অর্জন করা যাবে। আমি ইমেল ক্ষেত্রে সমস্যা ছিল এবং এটি মডেল স্তরে অনন্য করতে চেয়েছিলাম এবং এটি এরকম করে:

def clean(self):
    """
    Make sure that email field is unique
    """
    if MyUser.objects.filter(email=self.email):
        raise ValidationError({'email': _('This email is already in use')})

ত্রুটি বার্তাটি "ইমেল" নাম সহ ফর্ম ফিল্ড দ্বারা ক্যাপচার করা হয়


প্রশ্নটি একটি চর ক্ষেত্রের সর্বাধিক দৈর্ঘ্য বাড়ানোর বিষয়ে। এটি যদি ডাটাবেস দ্বারা প্রয়োগ করা হয়, তবে এই "সমাধান" সাহায্য করবে না। একটি ভিত্তি হ'ল বেস মডেলে দীর্ঘতম দৈর্ঘ্য নির্দিষ্ট করতে হবে এবং সেখানে সংক্ষিপ্ত দৈর্ঘ্য প্রয়োগের জন্য পরিষ্কার () পদ্ধতি ব্যবহার করা হবে।
ডিলানইং

0

আমার সমাধানটি পরবর্তীটির মতোই সহজ monkey patching, লক্ষ্য করুন আমি কীভাবে মডেলটিতে max_lengthঅ্যাট্রিবিউট nameফিল্ডটি পরিবর্তন করেছি LongNamedRestaurant:

class Place(models.Model):
   name = models.CharField(max_length=20)

class LongNamedRestaurant(Place):
    food_type = models.CharField(max_length=25)
    Place._meta.get_field('name').max_length = 255
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.