Question Remplacez save_model sur Django InlineModelAdmin


J'ai un modèle qui a un user champ qui doit être rempli automatiquement à partir de l'utilisateur actuellement connecté. Je peux le faire fonctionner comme spécifié ici si la user champ est dans un ModalAdmin standard, mais si le modèle avec lequel je travaille est dans un InlineModelAdmin et être sauvé de l'enregistrement d'un autre modèle à l'intérieur de l'Admin, cela ne prendra pas.


16
2017-11-13 21:52


origine


Réponses:


Voici ce que je pense être la meilleure solution. Il m'a fallu du temps pour le trouver ... cette réponse m'a donné les indices: https://stackoverflow.com/a/24462173/2453104

Sur votre admin.py:

class YourInline(admin.TabularInline):
    model = YourInlineModel
    formset = YourInlineFormset

    def get_formset(self, request, obj=None, **kwargs):
        formset = super(YourInline, self).get_formset(request, obj, **kwargs)
        formset.request = request
        return formset

Sur vos formulaires.py:

class YourInlineFormset(forms.models.BaseInlineFormSet):
    def save_new(self, form, commit=True):
        obj = super(YourInlineFormset, self).save_new(form, commit=False)
        # here you can add anything you need from the request
        obj.user = self.request.user

        if commit:
            obj.save()

        return obj

10
2018-04-13 00:38



Je sais que je suis en retard à la fête, mais voici ma situation et ce que j'ai imaginé, ce qui pourrait être utile à quelqu'un d'autre à l'avenir. 

J'ai 4 modèles en ligne qui nécessitent l'utilisateur actuellement connecté.

  • 2 en tant que created_by champ de type. (défini une fois lors de la création)
  • et les 2 autres en tant que closed_by champ de type. (uniquement sur condition)

J'ai utilisé la réponse fournie par rafadev et en fait un simple mixin ce qui me permet de spécifier le nom du champ utilisateur ailleurs.

Le formset générique dans forms.py

from django.forms.models import BaseInlineFormSet

class SetCurrentUserFormset(forms.models.BaseInlineFormSet):
    """
    This assume you're setting the 'request' and 'user_field' properties
    before using this formset.
    """
    def save_new(self, form, commit=True):
        """
        This is called when a new instance is being created.
        """
        obj = super(SetCurrentUserFormset, self).save_new(form, commit=False)
        setattr(obj, self.user_field, self.request.user)
        if commit:
            obj.save()
        return obj

    def save_existing(self, form, instance, commit=True):
        """
        This is called when updating an instance.
        """
        obj = super(SetCurrentUserFormset, self).save_existing(form, instance, commit=False)
        setattr(obj, self.user_field, self.request.user)
        if commit:
            obj.save()
        return obj

Classe Mixin dans votre admin.py

class SetCurrentUserFormsetMixin(object):
    """
    Use a generic formset which populates the 'user_field' model field
    with the currently logged in user.
    """
    formset = SetCurrentUserFormset
    user_field = "user" # default user field name, override this to fit your model

    def get_formset(self, request, obj=None, **kwargs):
        formset = super(SetCurrentUserFormsetMixin, self).get_formset(request, obj, **kwargs)
        formset.request = request
        formset.user_field = self.user_field
        return formset

Comment l'utiliser

class YourModelInline(SetCurrentUserFormsetMixin, admin.TabularInline):
    model = YourModel
    fields = ['description', 'closing_user', 'closing_date']
    readonly_fields = ('closing_user', 'closing_date')
    user_field = 'closing_user' # overriding only if necessary

Faites attention...

... comme ce code mixin définira l'utilisateur actuellement connecté pour chaque utilisateur. Si vous avez besoin que le champ soit rempli uniquement lors de la création ou d'une mise à jour spécifique, vous devez gérer cela dans votre méthode de sauvegarde de modèle. Voici quelques exemples:

class UserOnlyOnCreationExampleModel(models.Model):
    # your fields
    created_by = # user field...
    comment = ...

    def save(self, *args, **kwargs):
        if not self.id:
            # on creation, let the user field populate
            self.date = datetime.today().date()
            super(UserOnlyOnCreationExampleModel, self).save(*args, **kwargs)
        else:
            # on update, remove the user field from the list
            super(UserOnlyOnCreationExampleModel, self).save(update_fields=['comment',], *args, **kwargs)

Ou si vous avez seulement besoin de l'utilisateur si un champ particulier est défini (comme un champ booléen closed):

def save(self, *args, **kwargs):

    if self.closed and self.closing_date is None:
        self.closing_date = datetime.today().date()
        # let the closing_user field set
    elif not self.closed :
        self.closing_date = None
        self.closing_user = None # unset it otherwise

    super(YourOtherModel, self).save(*args, **kwargs)  # Call the "real" save() method.

Ce code pourrait probablement être bien plus générique car je suis relativement nouveau sur python mais c'est ce qui sera dans mon projet pour le moment.


4
2018-05-22 02:01



Seulement le save_model pour le modèle que vous éditez, vous devrez plutôt utiliser le post_save signal pour mettre à jour les données en ligne.

(Pas vraiment un doublon, mais essentiellement la même question est répondue dans Les modèles de modèles en ligne émettent-ils des signaux post_save? (django))


2
2018-01-31 09:48



J'ai eu un problème similaire avec un champ utilisateur que j'essayais de remplir dans un modèle en ligne. Dans mon cas, le modèle parent avait également le champ utilisateur défini, donc j'ai remplacé save sur le modèle enfant comme suit:

class inline_model:
    parent = models.ForeignKey(parent_model)
    modified_by = models.ForeignKey(User,editable=False) 
    def save(self,*args,**kwargs):
        self.modified_by = self.parent.modified_by
        super(inline_model,self).save(*args,**kwargs)

Le champ utilisateur a été initialement renseigné automatiquement sur le modèle parent en remplaçant save_model dans ModelAdmin pour le modèle parent et en lui affectant

obj.modified_by = request.user

Gardez à l'esprit que si vous avez également une page d'administration autonome pour le modèle enfant, vous aurez besoin d'un autre mécanisme pour que les champs parents et enfants modifiés soient synchronisés (par exemple, vous pouvez remplacer save_model sur l'enfant ModelAdmin et mettre à jour / enregistrer le champ modified_by sur le parent avant d'appeler save sur l'enfant).

Je n'ai pas trouvé un bon moyen de gérer cela si l'utilisateur n'est pas dans le modèle parent. Je ne sais pas comment récupérer request.user en utilisant des signaux (par ex. post_save), mais peut-être que quelqu'un d'autre peut donner plus de détails à ce sujet.


1
2017-08-16 15:27



Est-ce que l'autre modèle sauve l'utilisateur? Dans ce cas, vous pouvez utiliser le post_savesignal pour ajouter cette information à l'ensemble du modèle en ligne.


0
2017-11-14 15:09



Avez-vous essayé d’implémenter une validation personnalisée dans l’admin tel que décrit dans le Documentation? Remplacer la fonction clean_user () sur le modèle peut vous aider.

Une autre option plus complexe vient à l’esprit. Vous pouvez remplacer le modèle d'administration qui affiche le formulaire de modification. Le remplacement du formulaire de modification vous permet de créer une balise de modèle personnalisée qui transmet l'utilisateur connecté à ModelForm. Vous pourriez alors écrire une coutume init fonction sur le formulaire de modèle qui définit l'utilisateur automatiquement. Cette réponse fournit un bon exemple sur la façon de le faire, tout comme le lien sur b-list que vous référencez dans la question.


0
2017-11-20 04:26