两个详细信息表具有同一个主表(用户)的外键,如何限制ManyToManyField仅查看另一个详细信息表的子集

问题描述 投票:0回答:1

在 Django 中,我有一个 Order 和 Contact 表,它们都有一个指向 User 表的foreignKeyField,在 Order 表中,我希望 ManyToManyField 仅查看属于同一 User 的 Contact 表记录的子集,而不是 ManyToManyField 查看所有记录联系人表的记录。

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})

列表视图网址:

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 参数或如何在这种情况下使用可调用对象。 在此示例中 ({'cnt_user': user}) django 将 user 视为“foreignKey 对象”而不是“用户对象”,这会导致以下错误: 字段“id”需要一个数字,但得到了

对我来说,“这个问题”似乎并不罕见,我希望有一个简单的解决方案。 任何人都可以向我展示如何通过可调用对象或 Q 对象或任何其他方式(可能是代理模型)实现此目的的示例吗? 预先感谢。

django django-models
1个回答
0
投票

有不同的方法可以解决您在代码中面临的这个挑战,包括在您看来覆盖它,我相信实现它的最简单方法是通过使用

Q
对象,因为我仍然想使用
limit_choices_to
如你所愿。我们将创建的 Q 对象将允许您根据数据库中的用户 ID 过滤联系人。但是,我们需要在
limit_choices_to
参数中访问用户的 ID。实现此目的的一种方法是使用自定义管理器方法,通过这种方法,我们将重写整个 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
    


请原谅我发表了这么多评论,只是为了尽可能地表达清楚,如果您愿意,请不要忘记删除它们。祝你好运,伙计。

© www.soinside.com 2019 - 2024. All rights reserved.