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.