1
votes

Ajout de tous les utilisateurs d'un groupe comme choix dans un champ de modèle

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é?


0 commentaires

3 Réponses :


0
votes

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 ".


3 commentaires

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.



0
votes

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.


0 commentaires

1
votes

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)


1 commentaires

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.