Django does not render my forms' fields - django

I have this model in models.py:
class Life_events(models.Model):
patient = models.ForeignKey(Demographic)
HSCT_date = models.DateField('HSCT date',null=True,blank=True)
HSCT_outcome = models.CharField('HSCT outcome',max_length=100, null=True, blank=True)
partaker_in_clinical_trial= models.CharField('Partaker in clinical trial',max_length=200, null=True, blank=True)
date_of_input= models.DateField(null=True,blank=True)
def __str__(self):
return str(self.patient)
My forms.py contains:
class LifeEventsForm(forms.Form):
def __init__(self, *args, **kwargs):
super(LifeEventsForm, self).__init__(*args, **kwargs)
self.helper=FormHelper(self)
self.helper.field_class = 'col-md-8'
self.helper.label_class = 'col-md-3'
self.helper.layout = Layout(
Fieldset (
'<b>HSCT</b>',
Div(
#HTML(u'<div class="col-md-2"></div>'),
Div('HSCT_date',css_class='col-md-6'),
Div('HSCT_outcome',css_class="col-md-6"),
Div('partaker_in_clinical_trial', css_class='col-md-6'),
css_class='row',
),
),
FormActions(
Submit('submit', "Save changes"),
Submit('cancel',"Cancel")
),
)
self.helper.form_tag = False
self.helper.form_show_labels = True
class Meta:
model = Life_events
exclude = ['patient', 'date_of_input']
My views.py has:
my_life_ev = LifeEventsForm(prefix='life_ev')
return render_to_response('input.html', {'frm_life_ev': my_life_ev,}, context)
And my template file input.html has:
{%crispy frm_life_ev%}
When I render my template I can't see the form fields but only the word 'HSCT' which I have in Fieldset. My html code does not contain the fields at all.
Any ideas please?

Your form inherits from forms.Form, which does not know anything about models and ignores the Meta class. You should inherit from forms.ModelForm.

You need to declare a ModelForm. A simple Form won't do.
class LifeEventsForm(forms.ModelForm)

Related

Django3.1 error when I try to save post with tags

I have a view inside posts app where I try to save a post with tags. Whenever I add a new tag to the post, I get this error:
value error at create
My view is this one:
class PostCreateView(CreateView):
template_name = 'posts/create.html'
form_class = PostCreationForm
model = Post
def get_success_url(self):
return reverse('posts:detail', kwargs={"slug": self.object.slug})
def form_valid(self, form):
form.instance.user = self.request.user
form.save() # this is where the error occurs
tags = self.request.POST.get("tag").split(",")
for tag in tags:
current_tag = Tag.objects.filter(slug=slugify(tag))
if current_tag.count() < 1:
create_tag = Tag.objects.create(title=tag)
form.instance.tag.add(create_tag)
else:
existed_tag = Tag.objects.get(slug=slugify(tag))
form.instance.tag.add(existed_tag)
return super(PostCreateView, self).form_valid(form)
The form I'm using is as follow:
class PostCreationForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(PostCreationForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_method = "post"
self.helper.field_class = 'form-group'
self.helper.layout = Layout(
Field('title', css_class="form-control", placeholder='Post title'),
Field('content', css_class="form-control", placeholder='Post content'),
Field('category', css_class="form-control"),
Field('image', css_class="form-control"),
Field('tag', css_class="form-control", placeholder='tag1, tag2')
)
self.helper.add_input(Submit('submit', 'Create New Post', css_class='btn btn-underline-primary'))
tag = forms.CharField()
class Meta:
model = Post
fields = ['title', 'content', 'category', 'image', 'tag']
This is the Post model:
class Post(models.Model):
title = models.CharField(max_length=150, unique=True)
content = RichTextUploadingField()
# content = models.TextField()
publish_date = models.DateTimeField(auto_now_add=True)
image = models.ImageField(blank=True, null=True, upload_to='uploads/')
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
slug = models.SlugField(default="slug", editable=False)
category = models.ForeignKey(Category, on_delete=models.CASCADE, default=1, related_name='posts')
tag = models.ManyToManyField(Tag, related_name='posts', blank=True)
slider_post = models.BooleanField(default=False)
hit = models.PositiveIntegerField(default=0)
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(Post, self).save(*args, **kwargs)
def __str__(self):
return self.title
def post_tag(self):
return ', '.join(str(tag) for tag in self.tag.all())
def comment_count(self):
return self.comments.all().count()
How can I fix this error: "Field 'id' expected a number but got 't'." (where 't' is the first letter from my first tag: 'test'. If I use another tag, then the error display the first letter of that word).
Remove the field tag from fields = ['title', 'content', 'category', 'image', 'tag']
The reason this works is that by including it, Django automatically creates a ModelMultipleChoiceField named tag.
However, since you are manually handling extracting and saving the tags (using tags = self.request.POST.get("tag").split(",")), excluding it from Meta.fields guarantees that Django does not also try to handle this field.
As such, another solution would be to let Django handle the tag form field by completely removing your custom save() method.

How to change the rendered field in Django's ModelForm queryset?

I want to change the rendered field shown in a model form choicefield, based on some user selected feature, which is language in my case.
I've got a two models. Of the two, the 'Vastausvaihtoehto' model saves an answer in both english and finnish, saving it to the database. It also returns the finnish answer by default, because that's how I've defined the unicode function:
Model
class Vastausvaihtoehto(models.Model):
...
vastaus_fi = models.CharField(
verbose_name=_(u'Vastaus'),
max_length=256,
null=True,
blank=True,
)
vastaus_en = models.CharField(
verbose_name=_(u'Vastaus_en'),
max_length=256,
null=True,
blank=True,
)
...
def __unicode__(self):
return u'%s' % (self.vastaus_fi)
class Valinta(models.Model):
organisaatio = models.ForeignKey(
Organisaatio,
related_name=_(u'valinta'),
null=True,
blank=True,
on_delete=models.CASCADE,
)
kysymys = models.ForeignKey(
Kysymysvaihtoehto,
related_name=_(u'valinta'),
null=True,
blank=True,
)
vastausvaihtoehto = models.ForeignKey(
Vastausvaihtoehto,
related_name=_(u'valinta'),
null=True,
blank=True,
)
def __unicode__(self):
return u'%s' % (self.kysymys)
I also have a ModelForm, that I use to select the correct choices
Form
class ValintaForm(ModelForm):
class Meta:
model = Valinta
fields = '__all__'
widgets = {
'organisaatio':forms.HiddenInput(),
'kysymys':forms.HiddenInput(),
'vastausvaihtoehto':forms.RadioSelect(),
}
And here's my view:
View
class kysymys(View):
template_name = 'mytemplate.html'
success_url = 'something'
def get(self, request, pk, question_id, *args, **kwargs):
kysymys = Kysymysvaihtoehto.objects.get(kysymys_id=int(question_id))
vastausvaihtoehdot = Vastausvaihtoehto.objects.filter(kysymysvaihtoehto=kysymys)
if request.LANGUAGE_CODE == 'fi':
# What do I put here?
else:
# What do I put in here?
form = ValintaForm()
form.fields['vastausvaihtoehto'].queryset = vastausvaihtoehdot
form.fields['vastausvaihtoehto'].empty_label = None
return render(request, self.template_name, {
'form':form,
'kysymys':kysymys,
"pk":pk,
"question_id":question_id,
})
I've tried to query just some certain values using values and values_list, and set them as the ModelForm queryset:
#Like so:
answers_en = Vastausvaihtoehto.objects.filter(kysymysvaihtoehto=kysymys).values_list('pk','vastaus_en')
form.fields['vastausvaihtoehto'].queryset = answers_en
But that does not render the form correctly. Should I add a helper method to the 'Vastausvaihtoehto' model, which returns the english name when called?
I know it's possible to circumvent this by just not using ModelForms, but is there a way to do this while using a ModelForm?
Define your ModelForm with an __init__ method which will accept language and question_id as keyword arguments.
class ValintaForm(ModelForm):
class Meta:
model = Valinta
fields = '__all__'
widgets = {
'organisaatio':forms.HiddenInput(),
'kysymys':forms.HiddenInput(),
'vastausvaihtoehto':forms.RadioSelect(),
}
def __init__(self, *args, **kwargs):
language = kwargs.pop('language', None)
question_id = kwargs.pop('question_id')
super(ValintaForm, self).__init__(*args, **kwargs)
if language == "fi":
kysymys = Kysymysvaihtoehto.objects.get(kysymys_id=int(question_id))
vastausvaihtoehdot = Vastausvaihtoehto.objects.filter(kysymysvaihtoehto=kysymys)
self.fields['vastausvaihtoehto'].queryset = vastausvaihtoehdot
else:
# put your other conditions here
pass
In your views, when you initialize your form, pass the keyword arguments
form = ValintaForm(language=request.LANGUAGE_CODE, question_id=question_id)
Or if you think it is better, you can pass the whole queryset to the forms.
def __init__(self, *args, **kwargs):
qs = kwargs.pop('qs')
super(ValintaForm, self).__init__(*args, **kwargs)
self.fields['vastausvaihtoehto'].queryset = qs
Pass the query set when you initialize form
form = ValintaForm(qs=vastausvaihtoehdot)

Crispy form render manytomanyfield as checkboxes

I have a manytomany object in a model form which I want to be rendered as select fields under each other, but whatever I try, i keep getting them next to each other with crispy django forms
class ContactForm(forms.ModelForm):
choice = forms.ModelMultipleChoiceField(label=_('Request'), widget=forms.CheckboxSelectMultiple(),required=False,
queryset=ContactFormChoices.objects.all())
name = forms.CharField(label=_('Name'))
email = forms.EmailField(required=False, label=_('E-mail'))
phone_number = forms.CharField(required=False, label=_('Phone number'))
message = forms.CharField( widget=forms.Textarea , label=_('Message'))
def __init__(self, *args, **kwargs):
super(ContactForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.layout = Layout(
Field('name', css_class='input-xlarge'),
Field('email', css_class='input-xlarge'),
Field('phone_number', css_class='input-xlarge'),
Field('message', rows="3", css_class='input-xlarge'),
#'choice',
Field('choice'),
FormActions(
Submit('submit', _('Submit'), css_class="btn-primary")
)
)
class Meta:
model = ContactData
fields = ['name','message','email','phone_number','choice']
and the model:
class ContactFormChoices(models.Model):
'''
The contact form options to show
'''
text = models.CharField(max_length=256)
active = models.BooleanField(default=True)
def __unicode__(self):
return self.text
class ContactData(models.Model):
'''
The contact data (when customer fills in the contact form,
this is mailed and saved here
'''
name = models.CharField(max_length=256,help_text=_("Name"))
phone_number= models.CharField(max_length=256,null=True,blank=True,default=None)
email = models.EmailField(max_length=256,null=True,blank=True,default=None)
choice = models.ManyToManyField(ContactFormChoices,blank=True,default=None)
message = models.TextField()
def __unicode__(self):
return self.name
it looks like this:
Anybody any suggestion?
wow, after searching and trying a lot.... the answer seem to be very simple:
helper.layout = Layout(
Field('name', css_class='input-xlarge'),
Field('email', css_class='input-xlarge'),
Field('phone_number', css_class='input-xlarge'),
Field('message', rows="3", css_class='input-xlarge'),
PrependedText('choice', ''),
FormActions(
Submit('submit', _('Submit'), css_class="btn-primary")
)
)
and partly duplicate to this question and answer: BooleanField checkbox not render correctly with crispy_forms using bootstrap

Pass parameters to Django class meta

I am trying to display a form with Django, but I want to remove a field if the user language is 'en'. I would like to avoid doing it in Javascript or doing a second template just for it. So I wonder if it possible to pass parameter to the Meta() class of my UserForm() class. That way I could pass the user and check with an if statement his language.
Here is what I have so far:
class UserForm(forms.ModelForm):
first_name = forms.CharField(required=True, label=_('*First name'))
last_name = forms.CharField(required=True, label=_('*Last name'))
postal_code = FRZipCodeField(required=False, label=_('My postcode'))
birthday = forms.DateField(
widget=forms.DateInput(format='%d/%m/%Y'),
required=False,
input_formats=['%d/%m/%Y'],
label=_('My birthday'))
def __init__(self, *args, **kwargs):
super(UserForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Field('gender'),
Field('first_name'),
Field('last_name'),
Field('birthday'),
Field('country'),
Field('language'),
Field('payment_preference'),
Div('addr1', css_class='hidden', css_id='addr1_container'),
Div('addr2', css_class='hidden', css_id='addr2_container'),
Div('addr3', css_class='hidden', css_id='addr3_container'),
Div('postal_code', css_class='hidden', css_id='pc_container'),
Div('city', css_class='hidden', css_id='city_container'),
ButtonHolder(
Submit('save', _('update'), css_class='pull-right'),
)
)
class Meta():
model = User
fields = (
"first_name", "last_name", "gender", "birthday", "country",
"payment_preference", "addr1", "addr2", "addr3", "postal_code",
"city", "language",)
With an if, I could set two different fields variable in the Meta() class and so show two different forms for each user language.
You can't pass it into the Meta, but you can pass it into the __init__ and hide the field you want to hide:
class UserForm(forms.ModelForm):
# ... as before
def __init__(self, user, *args, **kwargs):
super(UserForm, self).__init__(*args, **kwargs)
if user.language == 'en':
self.fields['field_name'].widget = forms.HiddenInput()
# ... as before
Then when you call the form, pass the user as the first argument:
form = UserForm(request.user, ...)

Dynamic Form fields in `__init__` in Django admin

I want to be able to add fields to django admin form at runtime. My model and form:
#admin.py
class SitesForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(SitesForm, self).__init__(*args, **kwargs)
self.fields['mynewfield'] = forms.CharField()
class SitesAdmin(admin.ModelAdmin):
form = SitesForm
admin.site.register(Sites,SitesAdmin)
#model.py
class Sites(models.Model):
url = models.URLField(u'URL')
is_active = models.BooleanField(default=True, blank=True)
is_new = models.BooleanField(default=False, blank=True)
group = models.ForeignKey('SitesGroup')
config = models.TextField(blank=True)
Field mynewfield isn't displayed in form. Why?
You shouldn't be adding a new field to your form in that way, you can just do it as you would any other field and the form will contain both the Model's original fields and your new fields:
class SitesForm(forms.ModelForm):
mynewfield = forms.CharField(max_length=255, blank=True)
class Meta:
model = Sites
class SitesAdmin(admin.ModelAdmin):
form = SitesForm
admin.site.register(Sites, SitesAdmin)
Edit: Sorry, should have read what you had written a little better. If you want a dynamic field like that, then you need to do the following and it will do exactly what you want:
class SitesForm(forms.ModelForm):
class Meta:
model = Sites
def __init__(self, *args, **kwargs):
self.base_fields['mynewfield'] = forms.CharField(max_length=255, blank=True)
super(SitesForm, self).__init__(*args, **kwargs)
class SitesAdmin(admin.ModelAdmin):
form = SitesForm
admin.site.register(Sites, SitesAdmin)
It's the base_fields that gets composed by the metaclass that holds the fields that the form will use.
Solution:
class AdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(AdminForm, self).__init__(*args, **kwargs)
self.fields.insert(1, 'myfield', forms.CharField())
class MyAdmin(admin.ModelAdmin):
form = AdminForm
def get_fieldsets(self, request, obj=None):
return (
(None, {
'fields': (..., 'myfield',),
}),
)