Django: ModelForm with conditions

I am trying to create a form variable. By default, the player has level 0 and can simply change the name. Later, when he is level 1, he can change his name and avatar. When he is at the third level, he can change his name, avatar and job. Etc ...

Models.py:

class Player(models.Model):
    level = models.SmallIntegerField(default=0) 
    name = models.CharField(max_length=50)
    avatar = models.URLField(default='http://default-picture.com/01.png')
    job =  models.TextField(null=True)

      

Fomrs.py:

class ProfileForm(forms.ModelForm):

class Meta:
    model = Player
    fields = ['name', 'avatar', 'job']
    widgets = {
        'name': forms.TextInput(),
        'avatar': forms.TextInput(),
        'job': forms.Textarea(),
    }

      

Views.py:

def game(request, id):
    user = get_object_or_404(Player, id=id)
    if request.method == 'POST':
        form = ProfileForm(request.POST, instance=user)
        if form.is_valid():
            form.save()
            return HttpResponse('Success')
    else:
        form = ProfileForm(instance=user)
    return render(request, "page/template.html",
            {'form': form})

      

template.html:

{{ form }}

      

Can I add a condition to render a form before submitting to render render? Or do I need to do this in my conditional template?

I just want the instantiated object to have more or less positive elements in terms of one of these parameters (player level as an example).

+3


source to share


3 answers


You can overwrite the form method __init__

to conditionally remove or disable fields:



class ProfileForm(forms.ModelForm):
    ...
    def __init__(self, *args, **kwargs):
        super(ProfileForm, self).__init__(*args, **kwargs)
        if self.instance and self.instance.level < 3:
            self.fields['job'].disabled = True # still displays the field in the template
            # del self.fields['job'] # removes field from form and template
        if self.instance and self.instance.level < 1:
            self.fields['avatar'].disabled = True

      

+3


source


Django pre_save signal can be used to solve this problem. Before you save the model is called hook

on pre_save

. You can write conditions to check if the user is allowed to change fields.

You will also need to save a copy of the model to compare the state in pre_save

, this can be done using the post_init hook.



from django.dispatch import receiver , Signal
from django.db.models.signals import post_init , pre_save

@receiver(pre_init , sender = Player)
def cache(sender , instance , **kwargs):
    instance.__original_name = instance.name
    instance.__original_avatar = instance.avatar
    instance.__original_job = instance.job
@receiver(pre_save , sender= Player)
def check_update(sender , instance , **kwargs):
    if instance.level == 1:
        #Revert changes to avatar and job, and keep changes in the name
    if instance.level == 2:
        #Revert changes to job , and keep changes in the name and avatar
    if instance.level == 3:
        #Keep all changes

      

This way you can keep track of all update fields.

+1


source


You can do this customization of your form in init method

In the situations described earlier, you can do the following:

class ProfileForm(forms.ModelForm):

    class Meta:
        model = Player
        fields = ['name', 'avatar', 'job']
        widgets = {
            'name': forms.TextInput(),
            'avatar': forms.TextInput(),
            'job': forms.Textarea(),
        }

    def __init__(self, *args, **kwargs):
        super(ProfileForm, self).__init__(*args, **kwargs)
        # checking if an instance exist
        if self.instance.id:
            # then at this point you can set as read only the fields about each case

            if self.instance.level < 1:
                self.fields["avatar"].widget.attrs["readonly"] = True
                self.fields["job"].widget.attrs["readonly"] = True
            elif self.instance.level >= 1 and self.instance.level < 3:
                self.fields["job"].widget.attrs["readonly"] = True

      

+1


source







All Articles