Две детальные вкладки с внешним ключом к одной и той же главной таблице (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
    


Пожалуйста, извините меня за такое количество комментариев, это просто для того, чтобы сделать их более понятными, насколько это возможно, и не забудьте удалить их, если хотите. Удачи, приятель.

Вернуться на верх