كيف يمكنني تقييد مفاتيح خارجية الخيارات الكائنات ذات الصلة فقط في جانغو

StackOverflow https://stackoverflow.com/questions/232435

  •  04-07-2019
  •  | 
  •  

سؤال

لدي اثنين من طريقة الخارجية علاقة مشابهة لما يلي

class Parent(models.Model):
  name = models.CharField(max_length=255)
  favoritechild = models.ForeignKey("Child", blank=True, null=True)

class Child(models.Model):
  name = models.CharField(max_length=255)
  myparent = models.ForeignKey(Parent)

كيف يمكنني تقييد الخيارات الأم.favoritechild فقط الأطفال الذين الوالد هو نفسه ؟ حاولت

class Parent(models.Model):
  name = models.CharField(max_length=255)
  favoritechild = models.ForeignKey("Child", blank=True, null=True, limit_choices_to = {"myparent": "self"})

ولكن الذي يسبب واجهة الادارة عدم إدراج أي الأطفال.

هل كانت مفيدة؟

المحلول

لقد عدت للتو عبر ForeignKey.limit_choices_to في جانغو مستندات.غير متأكد حتى الآن كيف يعمل هذا ، ولكن قد يكون مجرد الشيء الصحيح هنا.

تحديث: ForeignKey.limit_choices_to يسمح لتحديد إما ثابت ، للاستدعاء أو س كائن للحد المسموح به الخيارات الرئيسية.ثابت واضح لا جدوى هنا ، لأنه لا يعرف شيئا عن الكائنات المعنية.

باستخدام للاستدعاء (دالة أو أسلوب فئة أو أي للاستدعاء كائن) تبدو واعدة أكثر.ولكن المشكلة في كيفية الوصول إلى المعلومات اللازمة من HttpRequest يبقى الكائن.باستخدام التخزين المحلي لمؤشر الترابط قد يكون حلا.

2.تحديث: هنا هو ما عملت بالنسبة لي:

أنا خلقت الوسيطة كما هو موضح في الرابط أعلاه.فإنه استخراج واحدة أو أكثر الحجج من طلب الحصول على جزء من قبيل "المنتج=1", و يخزن هذه المعلومات في موضوع السكان المحليين.

القادم هناك فئة الأسلوب في النموذج الذي يقرأ الموضوع المحلية متغير بإرجاع قائمة من معرفات للحد من اختيار حقل المفتاح الأجنبي.

@classmethod
def _product_list(cls):
    """
    return a list containing the one product_id contained in the request URL,
    or a query containing all valid product_ids if not id present in URL

    used to limit the choice of foreign key object to those related to the current product
    """
    id = threadlocals.get_current_product()
    if id is not None:
        return [id]
    else:
        return Product.objects.all().values('pk').query

من المهم العودة إلى استعلام يتضمن كل ما يمكن معرفات إذا لم يكن محددا بحيث العادي صفحات المشرف عمل طيب.

الخارجية حقل المفتاح ثم أعلن:

product = models.ForeignKey(
    Product,
    limit_choices_to={
        id__in=BaseModel._product_list,
    },
)

الصيد هو أن عليك أن تقدم هذه المعلومات إلى تقييد الخيارات عن طريق الطلب.أنا لا أرى وسيلة للوصول إلى "الذات" هنا.

نصائح أخرى

والطريقة "صحيحة" للقيام بذلك هو استخدام النموذج المخصص. من هناك، يمكنك الوصول إلى self.instance، وهو الكائن الحالي. مثال -

from django import forms
from django.contrib import admin 
from models import *

class SupplierAdminForm(forms.ModelForm):
    class Meta:
        model = Supplier
        fields = "__all__" # for Django 1.8+


    def __init__(self, *args, **kwargs):
        super(SupplierAdminForm, self).__init__(*args, **kwargs)
        if self.instance:
            self.fields['cat'].queryset = Cat.objects.filter(supplier=self.instance)

class SupplierAdmin(admin.ModelAdmin):
    form = SupplierAdminForm

وهذا "الحق" طريقة جديدة للقيام بذلك، على الأقل منذ جانغو 1.1 هي عن طريق تجاوز AdminModel.formfield_for_foreignkey (النفس، db_field، طلب **، kwargs).

HTTP: // docs.djangoproject.com/en/1.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.formfield_for_foreignkey

وبالنسبة لأولئك الذين لا يريدون لمتابعة الرابط أدناه هي وظيفة المثال الذي هو قريب لنماذج الأسئلة أعلاه.

class MyModelAdmin(admin.ModelAdmin):
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "favoritechild":
            kwargs["queryset"] = Child.objects.filter(myparent=request.object_id)
        return super(MyModelAdmin, self).formfield_for_manytomany(db_field, request, **kwargs)

وأنا فقط لست متأكدا من كيفية الحصول على الكائن الحالي الذي يتم تحريره. أتوقع أنه هو في الواقع على النفس في مكان ما ولكن لست متأكدا.

وهذا ليس كيف يعمل جانغو. هل فقط إنشاء العلاقة تسير في اتجاه واحد.

class Parent(models.Model):
  name = models.CharField(max_length=255)

class Child(models.Model):
  name = models.CharField(max_length=255)
  myparent = models.ForeignKey(Parent)

وإذا كنت تحاول الوصول إلى الأطفال من الوالد كنت ستفعل parent_object.child_set.all(). إذا قمت بتعيين related_name في مجال myparent، ثم وهذا هو ما كنت أشير إلى أنها. مثال: related_name='children'، ثم كنت ستفعل parent_object.children.all()

مستندات http://docs.djangoproject.com/ ، العلاقات احدة كثيرة إلى حمام / ديف / المواضيع / ديسيبل / نماذج / # للحصول على أكثر من ذلك.

إذا كنت بحاجة فقط القيود في جانغو واجهة المشرف, ربما هذا العمل.أنا بناء على هذا الجواب من منتدى آخر - على الرغم من أن يفعل ManyToMany العلاقات يجب أن تكون قادرة على استبدال formfield_for_foreignkey من أجل أن تعمل.في admin.py:

class ParentAdmin(admin.ModelAdmin):
    def get_form(self, request, obj=None, **kwargs):
        self.instance = obj
        return super(ParentAdmin, self).get_form(request, obj=obj, **kwargs)

    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
        if db_field.name == 'favoritechild' and self.instance:       
            kwargs['queryset'] = Child.objects.filter(myparent=self.instance.pk)
        return super(ChildAdmin, self).formfield_for_foreignkey(db_field, request=request, **kwargs)

هل تريد أن تقييد الخيارات المتاحة في واجهة المشرف عند إنشاء / تحرير مثيل النموذج؟

وإحدى الطرق للقيام بذلك هي التحقق من صحة النموذج. هذا يتيح لك رفع خطأ في واجهة المشرف إذا كان الحقل الأجنبي ليس هو الخيار الصحيح.

وبطبيعة الحال، والإجابة اريك هو الصحيح: أنت فقط بحاجة فعلا مفتاح أجنبي واحد، من الأطفال والآباء هنا

وBer: واضاف لقد التحقق من صحة لنموذج مماثل لهذا

class Parent(models.Model):
  name = models.CharField(max_length=255)
  favoritechild = models.ForeignKey("Child", blank=True, null=True)
  def save(self, force_insert=False, force_update=False):
    if self.favoritechild is not None and self.favoritechild.myparent.id != self.id:
      raise Exception("You must select one of your own children as your favorite")
    super(Parent, self).save(force_insert, force_update)

والذي يعمل بالضبط كيف أريد، ولكن سيكون من الجميل حقا إذا كان هذا التحقق يمكن أن تحد من الخيارات في القائمة المنسدلة في واجهة الادارة بدلا من التحقق من صحة بعد هذا الاختيار.

أنا أحاول أن أفعل شيئا من هذا القبيل.يبدو أن الجميع قائلا 'يجب أن يكون لديك فقط مفتاح خارجي طريقة واحدة' ربما يساء فهم ما تحاولين القيام به.

انه من العار limit_choices_to={"myparent":"النفس"} أردت أن أفعل لا يعمل...التي كانت نظيفة وبسيطة.للأسف "الذات" لا تحصل على تقييم يمر سهل السلسلة.

ربما اعتقدت أنني يمكن أن تفعل:

class MyModel(models.Model):
    def _get_self_pk(self):
        return self.pk
    favourite = models.ForeignKey(limit_choices_to={'myparent__pk':_get_self_pk})

ولكن للأسف أن يعطي خطأ لأن وظيفة لا يحصل مرت النفس arg :(

يبدو أن السبيل الوحيد هو وضع المنطق في جميع النماذج التي تستخدم هذا النموذج (أي تمرير queryset في الخيارات الخاصة بك formfield).والتي تتم بسهولة ، ولكن سيكون أكثر الجافة أن يكون هذا في نموذج المستوى.الخاص بك تجاوز الأسلوب حفظ النموذج يبدو وسيلة جيدة لمنع صالح من الخيارات من خلال الحصول على.

التحديث
أرى بلدي في وقت لاحق الجواب عن طريقة أخرى https://stackoverflow.com/a/3753916/202168

وثمة نهج بديل سيكون ليس لديهم "favouritechild" كيه كحقل على نموذج الأم.

وبدلا من ذلك هل يمكن أن يكون حقل منطقي is_favourite على الطفل.

ويمكن أن تساعد هذه: https://github.com/anentropic/django-exclusivebooleanfield

وبهذه الطريقة كنت تجنب المشكلة برمتها لضمان الأطفال يمكن أن يتم إلا المفضل لدى الأم التي ينتمون إليها.

وعرض رمز ستكون مختلفة قليلا ولكن منطق تصفية تكون واضحة.

في المشرف هل يمكن أن يكون حتى مضمنة لنماذج الطفل الذي يتعرض مربع is_favourite (إذا كان لديك سوى عدد قليل من الأطفال في الأصل) وإلا فإن المشرف سيتعين القيام به من جانب الطفل.

from django.contrib import admin
from sopin.menus.models import Restaurant, DishType

class ObjInline(admin.TabularInline):
    def __init__(self, parent_model, admin_site, obj=None):
        self.obj = obj
        super(ObjInline, self).__init__(parent_model, admin_site)

class ObjAdmin(admin.ModelAdmin):

    def get_inline_instances(self, request, obj=None):
        inline_instances = []
        for inline_class in self.inlines:
            inline = inline_class(self.model, self.admin_site, obj)
            if request:
                if not (inline.has_add_permission(request) or
                        inline.has_change_permission(request, obj) or
                        inline.has_delete_permission(request, obj)):
                    continue
                if not inline.has_add_permission(request):
                    inline.max_num = 0
            inline_instances.append(inline)

        return inline_instances



class DishTypeInline(ObjInline):
    model = DishType

    def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
        field = super(DishTypeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
        if db_field.name == 'dishtype':
            if self.obj is not None:
                field.queryset = field.queryset.filter(restaurant__exact = self.obj)  
            else:
                field.queryset = field.queryset.none()

        return field

class RestaurantAdmin(ObjAdmin):
    inlines = [
        DishTypeInline
    ]
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top