escolhas limite para foreignKey utilizando middleware
-
09-09-2019 - |
Pergunta
Eu estou olhando para fazer algo como isto:
Modelo limit_choices_to = { 'usuário': user}
com algumas diferenças.
Alguns modelos podem explicar:
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)
Nota:. A sintaxe não pretende necessariamente correto, mas ilustrativo
Agora, eu tenho tido algum sucesso em conseguir o usuário atual, utilizando middleware, como algumas respostas sobre SO retratam, no entanto, eu estava esperando que eu poderia começar o trabalho atual, via request.POST, de tal forma que, se o atividade foram salvos, eu seria capaz de discernir o trabalho atual e, portanto, o subconjunto de usuários como operadores, que por sua vez, seria o conjunto de usuário para escolher no modelo de actividade.
Em outras palavras, com base nas seleções de um ManyToManyField no campo pai, oferta que sub-seleção para o campo filho, ou, se John, Jim, Jordânia e Jesse trabalhou em um trabalho, escolha apenas os nomes a descrever o trabalho em uma atividade, dentro e atribuído a esse trabalho.
BTW, aqui está a minha tentativa ingênua em middleware:
# 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)
eo modelo Atividade:
operators = modes.ManyToManyField(User, limit_choices_to=dict(Q(User.objects.filter(job==threadlocals.get_current_job)))
Obrigado.
Solução
Ok, eu odeio jogar uma chave de macaco em suas obras, mas a sério não precisa usar threadlocals hacks.
O usuário está no objeto de solicitação que significa que não há necessidade de extraí-lo usando middleware. É passado como um argumento para cada exibição.
O truque para limitar suas escolhas de formulário é, portanto, alterar dinamicamente o queryset usado na forma em oposição a fazer um limite de opções no modelo.
Assim, seus formulário esta aparência:
# forms.py
from django import forms
from project.app.models import Activity
class ActivityForm(forms.ModelForm):
class Meta:
model Activity
e sua visão será parecido com este:
# views.py
...
form = ActivityForm(instance=foo)
form.fields['job_operators'].queryset = \
User.objects.filter(operators__set=bar)
...
Este é apenas um esboço rápido, mas deve dar-lhe a idéia geral.
Se você está se perguntando como evitar threadlocals na administração, leia Usuários e administrador por James Bennett.
Editar: forma útil truques em Django por Collin Grady também mostra um exemplo de configuração dinamicamente o queryset no método de formulário __init__, que é mais limpo do que o meu exemplo acima.
Outras dicas
Mas o que sobre os campos que definem com list_filter? Eles só respeitar limit_choices_to. Então, se você quiser limitar as escolhas de Django admin filtra você tem que usar limit_choices_to e alguns Middleware para filtrá-los com base no usuário atual. Ou há uma solução mais fácil?
chaves de macaco acolher amigo!
Eu também tinha encontrado uma outra maneira, embora pareça menos direta:
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',)
Eu acho que os seus caminhos é melhor! Graças montes!