Две детальные вкладки с внешним ключом к одной и той же главной таблице (User), как ограничить поле ManyToManyField для просмотра только подмножества другой детальной вкладки
В Django у меня есть таблица Order и Contact, которые обе имеют foreignKeyField к таблице User, в таблице Order я хочу, чтобы поле ManyToManyField видело только подмножество записей таблицы Contact, которые принадлежат тому же пользователю, вместо того чтобы поле ManyToManyField видело все записи таблицы Contact.
class Contact(models.Model):
cnt_user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
name = models.CharField(max_length=30, blank=False, null=True)
class Order(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='order_set')
ref_code = models.CharField(max_length=20, blank=True, null=True)
### ManyToManyField should only see subset of contacts that have same user.
contacts = models.ManyToManyField(Contact, blank=True, limit_choices_to={'cnt_user': user})
Функция представления, которая создает или обновляет запись заказа:
def create_or_update_order(request, cur_pk=None, mst_pk=None):
order = None
masterpk = None
verbal = 'New'
error = None
sender = None
if cur_pk:
print('cur_pk recieved')
# When Get or Post and jet cur_pk is criterium for edit state
verbal = 'Edit'
# If pk is given, get the record object
try:
order = Order.objects.get(pk=cur_pk)
# Haal detail records op
#if hoedanigheid != 'ParentCurrentNoDetail':
# = order.Detail_Subset.all()
except Order.DoesNotExist:
pass
## When a mst_pk is given
if mst_pk:
masterpk = mst_pk
else:
## Check if there is a masterpk
try:
if order.user_id:
masterpk = order.user_id
except AttributeError:
pass
if request.method == 'POST':
print('in post')
sender = request.POST.get('ctrl')
# Beeing in post is criterium for edit state
verbal = 'Edit'
form = OrderForm(request.POST, request.FILES, instance=order)
# If pk is given it is about an update
if cur_pk:
print('has pk')
print(cur_pk)
if form.is_valid():
print('orderform is valid')
form.save()
else:
print('orderform is not valid')
else:
if form.is_valid():
order = form.save(commit=False)
#order.creator = request.user
order.ordered_date = timezone.now()
if masterpk:
order.user_id = masterpk
order.save()
else:
error = 'Form not valid, new record not saved'
else:
print('in get')
form = OrderForm(instance=order)
ctrl = {'verbal': verbal, 'masterpk': masterpk, 'error': error, 'from': sender}
if sender == 'checkout':
return redirect('core:checkout')
else:
return render(request, 'core/create_or_update_order.html', {'form': form, 'ctrl': ctrl })
Урлы к этому представлению:
path("create/order/<int:mst_pk>/", order.create_or_update_order, name="create_order"),
path("edit/order/<int:cur_pk>/", order.create_or_update_order, name="edit_order"),
Вид списка для заказов:
def list_by_user_order(request, mst_pk):
user = None
orders = None
verbal = 'List'
Error = None
# Get all records
try:
user = theUser.objects.get(pk=mst_pk)
except theUser.DoesNotExist:
pass
orders = user.order_set.all()
ctrl = {'verbal': verbal, 'error': Error, 'masterpk': mst_pk}
return render(request, 'core/list_order_by_user.html',
{'ctrl': ctrl, 'orders': orders,
'user': user})
URL для просмотра списка:
path("list_by_theUser/order/<int:mst_pk>/", order.list_by_user_order, name="list_by_theUser_order"),
Форма заказа (для удобочитаемости некоторые коды (поля) опущены:
)class OrderForm(forms.ModelForm):
required_css_class = 'required'
class Meta:
model = Order
fields = ( 'ord_contactpersonen',)
Я не могу понять, как задать параметр limit_choices_to или как использовать callable в этом случае. В данном примере ({'cnt_user': user}) django воспринимает user как "foreignKey объект", а не как "user объект", что приводит к следующей ошибке: Поле 'id' ожидало число, а получило <django.db.models.fields.related.ForeignKey: user>.
Для меня "эта проблема" не кажется редким сценарием, я надеюсь, что есть простое решение. Может ли кто-нибудь показать мне пример того, как это сделать, либо с помощью callable, либо с помощью Q-объекта, либо любым другим способом (возможно, с помощью прокси-модели)? Заранее спасибо.
Существуют различные подходы к решению этой проблемы, с которой вы столкнулись в своем коде, включая переопределение в представлении, и я считаю, что самый простой способ достичь этого - использовать объект Q
, поскольку я все еще хочу использовать limit_choices_to
, как вы хотели. Объект Q, который мы создадим, позволит вам фильтровать контакты на основе идентификатора пользователя в вашей базе данных. Однако нам нужно будет получить доступ к идентификатору пользователя в параметре limit_choices_to
. Один из способов сделать это - использовать пользовательский метод менеджера, при таком подходе, поскольку мы собираемся переписать весь фрагмент models.py, мы уверены, что ограничили выбор для поля contacts
в Order
вашей ManyToManyField
модели.
ваша переписанная версия кода:
from django.conf import settings
from django.db import models
from django.db.models import Q
class ContactManager(models.Manager):
def get_queryset(self) -> models.QuerySet:
""" This method overrides the default get_queryset() method provided by the base
models.Manager class. It returns a QuerySet
of objects based on the specified filter conditions"""
queryset = super().get_queryset().\
filter(user_id = models.F('order__user_id'))
return queryset
# NB : filter(user_id=models.F('order__user_id')) above, filters the queryset based on
# the condition that the user_id field should be equal to the value of models.F('order__user_id')
class Contact(models.Model):
cnt_user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
name = models.CharField(max_length=30, blank=False, null=True)
objects = ContactManager()
def __str__(self) -> str:
return self.cnt_user.username
class Order(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='order_set'
)
ref_code = models.CharField(max_length=20, blank=True, null=True)
### ManyToManyField should only see subset of contacts that have same user.
contacts = models.ManyToManyField(
Contact,
limit_choices_to=Q(
user_id=models.F('cnt_user_id'),
blank=True
))
# a human readable object setup instead of the data been #
# returned in as object1, object2 etc. in your admin panel.
def __str__(self):
return self.user.username
Пожалуйста, извините меня за такое количество комментариев, это просто для того, чтобы сделать их более понятными, насколько это возможно, и не забудьте удалить их, если хотите. Удачи, приятель.
Похоже, способ задать запрос поля выбора с внешним ключом (или многими) в представлении все-таки существует!
Удивительно, что я не нашел этот ответ раньше.
Anser здесь:
Как отфильтровать варианты ForeignKey в Django ModelForm?
В моем случае:
(В представлении:)
form.fields["contacts"].queryset = contact.objects.filter(cnt_user_id=order.user_id)