كيف يمكنني تقييد مفاتيح خارجية الخيارات الكائنات ذات الصلة فقط في جانغو
-
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).
وبالنسبة لأولئك الذين لا يريدون لمتابعة الرابط أدناه هي وظيفة المثال الذي هو قريب لنماذج الأسئلة أعلاه.
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
]