问题
I'm trying to create a form variable. As default Player have the level 0 and he can just change is name. Later when he is level 1, he can change is name and is avatar. When he is level 3, he can change is name, is avatar and is 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 }}
It's possible to add condition for the render of the form before send it to the render engime ? Or I need to do it in my Template with conditions ?
I just when to allow the instanced object to have more or less positibilies in terms of one of these parameters (in the exemple is the level of the player).
回答1:
You can overwrite the form's __init__
method to remove or disable fields conditionally:
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
回答2:
Django pre_save signal can be used to solve this.
Before saving the model, the hook
on pre_save
will be called. You can write conditions in there to check if the user is allowed to change the fields.
You'll also need to store a copy of the model to compare the state in pre_save
, it can be done through 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 the updating fields.
回答3:
You can achieve this customizing your form in its init method
To the situations described Before, you can do some as:
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
来源:https://stackoverflow.com/questions/43001425/django-modelform-with-conditions