【问题标题】:limit choices to foreignkey using middleware使用中间件将选择限制为外键
【发布时间】:2009-02-25 22:37:51
【问题描述】:

我想做这样的事情:

Model limit_choices_to={'user': user}

有些不同。

一些模型可能会解释:

class Job(models.Model):
    name = models.CharField(max_length=200)
    operators = models.ManyToManyField(User)

class Activity(models.Model):
    job = models.ForeignKey(Job)
    job_operators = models.ManyToManyField(User, limit_choices_to={user: Job.operators} blank=True, null=True)

注意:语法并不一定是正确的,而是说明性的。

现在,我在使用中间件获得当前用户方面取得了一些成功,正如 SO 上的一些答案所描述的那样,但是,我希望我可以通过 request.POST 获得当前的作业,这样,如果Activity 已保存,我将能够识别当前的 Job,因此作为操作员的用户子集将成为 Activity 模型中可供选择的用户集。

换句话说,根据父字段中的 ManyToManyField 选择,将该子选择提供给子字段,或者,如果 John、Jim、Jordan 和 Jesse 从事工作,则仅从这些名称中选择描述一个 Activity 上的工作,在该 Job 内并归属于该 Job。

顺便说一句,这是我对中间件的天真尝试:

# threadlocals middleware
try:
    from threading import local
except ImportError:
    from django.utils._threading_local import local

_thread_locals = local()
def get_current_user():
    return getattr(_thread_locals, 'user', None)

def get_current_job():
    return getattr(_thread_locals, 'job', None)

class ThreadLocals(object):
    """Middleware that gets various objects from the
    request object and saves them in thread local storage."""
    def process_request(self, request):
        _thread_locals.user = getattr(request, 'user', None)
        _thread_locals.job = getattr(request.POST["job"], 'job', None)

以及活动模型:

operators = modes.ManyToManyField(User, limit_choices_to=dict(Q(User.objects.filter(job==threadlocals.get_current_job)))

谢谢。

【问题讨论】:

  • 顺便说一句,我发现这可以与硬编码的 pk 一起工作,作为我目标的进一步说明......当然挑战是要知道识别 pk,也许使用中间件? limit_choices_to=Q(username__in=Job.objects.get(pk=1).operators.values('username'))

标签: django post middleware limit-choices-to


【解决方案1】:

好吧,我不想在你的作品中使用活动扳手,但你真的不需要使用 threadlocals hack。

用户在请求对象中,这意味着不需要使用中间件来提取它。它作为参数传递给每个视图。

因此,限制表单选择的技巧是动态更改表单中使用的查询集,而不是在模型中进行限制选择。

所以你的表单看起来像这样:

# forms.py
from django import forms
from project.app.models import Activity
class ActivityForm(forms.ModelForm):
    class Meta:
        model Activity

您的视图将如下所示:

# views.py
...
form = ActivityForm(instance=foo)
form.fields['job_operators'].queryset = \
    User.objects.filter(operators__set=bar)
...

这只是一个简单的草图,但应该会给你一个大致的概念。

如果您想知道如何在管理员中避免线程本地化,请阅读 James Bennett 的 Users and the admin

编辑:Useful form tricks in Django by Collin Grady 还展示了以 __init__ 方法的形式动态设置查询集的示例,这比我上面的示例更简洁。

【讨论】:

  • 您也可以在管理员中通过覆盖 ModelAdmin 上的正确方法来执行类似的操作——它们中的大多数也将请求作为参数。
  • @James Bennett 你能举个例子来说明在 ModelAdmin 中覆盖哪个方法吗?谢谢
  • @James Bennett:没关系。找到这个:docs.djangoproject.com/en/dev/ref/contrib/admin/…
【解决方案2】:

但是您使用 list_filter 定义的字段呢?他们只尊重limit_choices_to。因此,如果您想限制 Django 管理过滤器的选择,您必须使用 limit_choices_to 和一些中间件来根据当前用户过滤它们。还是有更简单的解决方案?

【讨论】:

    【解决方案3】:

    猴子扳手欢迎朋友!

    我还找到了另一种方法,虽然它看起来不那么直接:

    class ActivityForm(forms.ModelForm):
        def __init__(self, *args, **kwargs):
            super(ActivityForm, self).__init__(*args, **kwargs)
    
            if self.initial['job_record']:
                jr = JobRecord.objects.get(pk=self.initial['job_record'])
                self.fields['operators'].queryset = jr.operators
    
        class Meta:
            model = Activity
            exclude = ('duration',)  
    

    我认为你的方法更好!多谢!

    【讨论】:

    • 但是,我意识到它现在只适用于新表单......而不是在编辑对象时......我必须修改。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-25
    • 2010-09-18
    • 1970-01-01
    • 2015-12-01
    • 2016-08-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多