2009-02-25 2 views
3

Я ищу, чтобы сделать что-то вроде этого:ограничить выбор для ForeignKey с использованием промежуточного программного

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 изображают, однако, я надеялся, что могу получить текущий Job через request.POST, чтобы, если активность была сохранена, я мог бы распознать текущее задание, и поэтому подмножество пользователей в качестве операторов, которое оно будет поворачивать, будет выбрано пользователем в модели Activity.

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

Кстати, вот моя наивная попытка в ПО промежуточного слоя:

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

Спасибо.

+0

Как я уже сказал, это работало с жестко закодированным pk, как еще одна иллюстрация моей цели ... Задача, конечно же, состоит в том, чтобы знать разницу pk, возможно, используя промежуточное ПО? limit_choices_to = Q (username__in = Job.objects.get (pk = 1) .operators.values ​​('username')) –

ответ

2

Хорошо, я ненавижу бросать в ваши работы маркер обезьяны, но вам серьезно не нужно использовать халаты threadlocals.

Пользователь находится в объекте запроса, что означает, что нет необходимости извлекать его с помощью промежуточного программного обеспечения. Он передается как аргумент для каждого представления.

Таким образом, трюк, ограничивающий выбор формы, динамически меняет выбор запроса, используемый в форме, а не делает выбор пределов в модели.

Так что ваша форма выглядит следующим образом:

# 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 в администраторе, пожалуйста, прочитайте Users and the admin Джеймсом Беннеттом.

Редактировать:Useful form tricks in Django Коллин Грейди также показывает пример динамически установки QuerySet в методе формы __init__, который чище, чем мой пример выше.

+2

Вы также можете делать такие вещи в админе, переопределяя правильные методы на вашем ModelAdmin - наиболее из них также получают запрос в качестве аргумента. –

+0

@James Bennett Можете ли вы привести пример того, какой метод переопределить в ModelAdmin? Спасибо –

+0

@ Джеймс Беннет: Ничего. Нашел это: http://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.formfield_for_foreignkey –

0

Ключи для обезьян приветствуются друг!

Я также нашел еще один способ, хотя это кажется менее прямым:

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

Я думаю, что ваши пути лучше! Спасибо, кучи!

+0

однако я понял, что теперь он работает только для новой формы ... а не при редактировании объекта ... Я должен пересмотреть. –

2

Но как насчет полей, которые вы определяете с помощью list_filter? Они уважают только limit_choices_to. Поэтому, если вы хотите ограничить выбор фильтров Django Admin, вы должны использовать limit_choices_to и некоторые Middleware для фильтрации их на основе текущего пользователя. Или есть более легкое решение?