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