J'essaie de restreindre un champ dans l'un de mes modèles à un choix de tous les utilisateurs qui sont membres d'un groupe particulier. Un autre champ a tous les utilisateurs enregistrés un choix. Cela fonctionne bien dans le shell:
O:\somedude\Documents\Python\Django\testcases>manage.py makemigrations
Traceback (most recent call last):
File "O:\somedude\Documents\Python\Django\testcases\manage.py", line 15, in <module>
execute_from_command_line(sys.argv)
File "C:\Program Files\Python36\lib\site-packages\django\core\management\__init__.py", line 381, i
n execute_from_command_line
utility.execute()
File "C:\Program Files\Python36\lib\site-packages\django\core\management\__init__.py", line 357, i
n execute
django.setup()
File "C:\Program Files\Python36\lib\site-packages\django\__init__.py", line 24, in setup
apps.populate(settings.INSTALLED_APPS)
File "C:\Program Files\Python36\lib\site-packages\django\apps\registry.py", line 112, in populate
app_config.import_models()
File "C:\Program Files\Python36\lib\site-packages\django\apps\config.py", line 198, in import_mode
ls
self.models_module = import_module(models_module_name)
File "C:\Program Files\Python36\lib\importlib\__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 994, in _gcd_import
File "<frozen importlib._bootstrap>", line 971, in _find_and_load
File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 678, in exec_module
File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
File "O:\somedude\Documents\Python\Django\testcases\testman\models.py", line 68, in <module>
class TestPlan(models.Model):
File "O:\somedude\Documents\Python\Django\testcases\testman\models.py", line 70, in TestPlan
tm_users = User.objects.filter(groups__name='Testmanager')
File "C:\Program Files\Python36\lib\site-packages\django\db\models\manager.py", line 82, in manage
r_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "C:\Program Files\Python36\lib\site-packages\django\db\models\query.py", line 844, in filter
return self._filter_or_exclude(False, *args, **kwargs)
File "C:\Program Files\Python36\lib\site-packages\django\db\models\query.py", line 862, in _filter
_or_exclude
clone.query.add_q(Q(*args, **kwargs))
File "C:\Program Files\Python36\lib\site-packages\django\db\models\sql\query.py", line 1263, in ad
d_q
clause, _ = self._add_q(q_object, self.used_aliases)
File "C:\Program Files\Python36\lib\site-packages\django\db\models\sql\query.py", line 1287, in _a
dd_q
split_subq=split_subq,
File "C:\Program Files\Python36\lib\site-packages\django\db\models\sql\query.py", line 1164, in bu
ild_filter
lookups, parts, reffed_expression = self.solve_lookup_type(arg)
File "C:\Program Files\Python36\lib\site-packages\django\db\models\sql\query.py", line 1028, in so
lve_lookup_type
_, field, _, lookup_parts = self.names_to_path(lookup_splitted, self.get_meta())
File "C:\Program Files\Python36\lib\site-packages\django\db\models\sql\query.py", line 1365, in na
mes_to_path
if field.is_relation and not field.related_model:
File "C:\Program Files\Python36\lib\site-packages\django\utils\functional.py", line 37, in __get__
res = instance.__dict__[self.name] = self.func(instance)
File "C:\Program Files\Python36\lib\site-packages\django\db\models\fields\related.py", line 94, in
related_model
apps.check_models_ready()
File "C:\Program Files\Python36\lib\site-packages\django\apps\registry.py", line 137, in check_mod
els_ready
raise AppRegistryNotReady("Models aren't loaded yet.")
django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.
O:\somedude\Documents\Python\Django\testcases>
Mais si je mets le même code dans le modèle:
class TestPlan(models.Model):
all_users = User.objects.all()
all_user_choices = ((x.username, x.get_full_name()) for x in all_users)
tm_users = User.objects.filter(groups__name='Testmanager')
tm_user_choices = ((x.username, x.get_full_name()) for x in tm_users)
tp_title = models.CharField(max_length=200, verbose_name='Title')
tp_manager = models.CharField(max_length=100, blank=True,
verbose_name='Test Manager',
choices=tm_user_choices)
tp_tester = models.CharField(max_length=100, blank=True,
verbose_name='Tester',
choices=all_user_choices)
def __str__(self):
return f'{self.tp_title}'
J'obtiens moins de trace utile (pour moi) de la pile:
>>> from django.contrib.auth.models import User, Group >>> all_users = User.objects.all() >>> all_users <QuerySet [<User: somedude>, <User: test>, <User: testma>]> >>> tm_users = User.objects.filter(groups__name='Testmanager') >>> tm_users <QuerySet [<User: somedude>, <User: testma>]>
Cela fonctionne quand je fais User.objects.all () mais ça tombe quand j'essaye de filtrer les utilisateurs par groupe. Je n'ai vraiment aucune idée de ce que je fais de mal ici. Est-ce un bug ou une fonctionnalité?
3 Réponses :
La propriété du champ "choix" ne peut pas être dynamique, doit nécessairement être un ensemble statique, par exemple:
MONTH_CHOICES = (
("JANUARY", "January"),
("FEBRUARY", "February"),
("MARCH", "March"),
# ....
("DECEMBER", "December"),
)
Si vous avez besoin de cette restriction, vous pouvez valider ce cas dans le " django form "ou logique de" sérialiseur DRF ".
Mais alors comment ça marche pour User.objects.all ()? C'est aussi dynamique.
Bien sûr, lorsque vous exécutez "User.objects.all ()", le résultat est dynamique, car il dépend des enregistrements de la base de données.
Ma question est la suivante: pourquoi User.objects.all () fonctionne-t-il, alors que User.objects.filter (groups__name = 'Testmanager') ne fonctionne pas? Ils sont tous les deux dynamiques, donc selon votre explication, aucun ne devrait fonctionner.
Bien que je n'ai pas trouvé de réponse à la question initiale, j'ai résolu le problème sous-jacent. La façon de faire est de définir les champs du modèle comme des clés étrangères:
class TestPlan(models.Model):
tp_title = models.CharField(max_length=200, verbose_name='Title')
tp_manager = models.ForeignKey(User, on_delete=models.SET_NULL,
blank=True, null=True, verbose_name='Test Manager',
limit_choices_to={'groups__name': 'Testmanager'},
related_name='testmanager_set')
tp_tester = models.ForeignKey(User, on_delete=models.SET_NULL,
blank=True, null=True, verbose_name='Tester')
Le paramètre limit_choices_to fait la même chose que User.objects.filter (groups__name = 'Testmanager') dans l'article d'origine . Le paramètre related_name est nécessaire car j'ai maintenant deux champs de clé étrangère sur le même modèle.
Vous devrez peut-être remplacer la méthode de sauvegarde et la méthode de nettoyage:
class TestPlanForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
tm_users = User.objects.filter(groups__name='Testmanager')
tm_user_choices = ((x.username, x.get_full_name()) for x in tm_users)
self.fields['tp_manager'].choices = tm_user_choices
class Meta:
model = TestPlan
fields = '__all__'
Django Docs: https://docs.djangoproject.com/en/2.0/ref/models/instances/#django.db.models.Model .clean
Sinon, vous pouvez utiliser un ModelForm et écrire une fonction propre, pour implémenter votre vérification de validation.
Si vous devez utilisez un formulaire pour pouvoir gérer facilement les choix dynamiques.
from django.core.exceptions import ValidationError
def clean(self):
if self.tp_manager and not self.tp_manager.groups.filter(name='Testmanager').exists():
raise ValidationError({'tp_manager': 'ValidationError Msg'})
def save(self, *args, **kwargs):
self.full_clean()
return super().save(*args, **kwargs)
J'aime la deuxième approche. Mais je pense que le faire au niveau du modèle est mieux car cela garantit l'intégrité référentielle dès le départ.