使用中间件限制对外键的选择

时间:2009-02-25 22:37:52

标签: django post middleware limit-choices-to

我希望做这样的事情:

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获取当前的Job,这样,如果保存了活动,我将能够识别当前的作业,因此,作为运算符的用户子集将成为用户可以在活动模型中进行选择。

换句话说,根据父字段中ManyToManyField的选择,将子选择提供给子字段,或者,如果John,Jim,Jordan和Jesse在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)))

谢谢。

3 个答案:

答案 0 :(得分:2)

好吧,我讨厌在你的作品中扔一把猴子扳手,但你真的不需要使用threadlocals hacks。

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

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

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

# 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)
...

这只是一个快速草图,但应该给你一般的想法。

如果您想知道如何在管理员中避免使用threadlocals,请阅读James Bennett撰写的Users and the admin

Collin Grady的

编辑 Useful form tricks in Django还显示了一个以__init__形式动态设置查询集的示例,该方法比上面的示例更清晰。

答案 1 :(得分:2)

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

答案 2 :(得分:0)

Monkey Wrenches欢迎朋友!

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

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',)  

我认为你的方式更好!谢谢你!