ImageField preview in crispy forms layout - django

I'd like to display the image from an ImageField. I'm using Django crispy forms. It seems I need to use the HTML layout helper, but I'm not sure how to access a template variable here.
The following renders a blank image tag:
HTML('<img src="{{ logo.url }}" />')
Am I referencing this wrong, or is there a better method in crispy forms?
Here is the full code so you can get a better idea:
forms.py
class CompanyForm(forms.Form):
name = forms.CharField(required=False, label="Company name")
logo = forms.ImageField(required=False, label="Logo")
# form layout
helper = FormHelper()
helper.form_class = 'form-horizontal'
helper.layout = Layout(
Div(
'name',
HTML('<img src="{{ logo.url }}" />'),
css_class='span4',
),
FormActions(
Submit('submit', 'Save', css_class="btn-primary pull-right"),
)
)
models.py
class Company(models.Model):
name = models.CharField(max_length=100)
logo = models.ImageField(upload_to = 'logos/', null=True)
views.py
I'm setting the initial values of the form in my view:
...
profile = Company.objects.get(name="foo")
form = CompanyForm(initial={'name': profile.name, 'logo': profile.logo, })
context = {
'profile_form' : form,
}
return render_to_response('dashboard/company-profile.html', context_instance=RequestContext(request, context))
company-profile.html
{% load crispy_forms_tags %}
<div class="row">
{% crispy profile_form %}
</div>

Funny, I was just working on the same issue today on a Inline image upload formset, I ended up with this implementation:
class ImageModelForm(forms.ModelForm):
class Meta:
model = ImageModel
def __init__(self, *args, **kwargs):
super(ImageModelForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_tag = False
self.helper.disable_csrf = True
self.helper.layout = Layout(
'title',
'description',
'imagefile',
HTML("""{% if form.imagefile.value %}<img class="img-responsive" src="{{ MEDIA_URL }}{{ form.imagefile.value }}">{% endif %}""", ),
'flag_featured',
)

People who are seeing this from-after 2020, the this is fixed since maybe v1.10, v.1.9 or nearby.
I've had same issue with v.1.8.

further to #petkostas answer, specifically on the image reload comment from #Banjer:
- i also had this problem with a form and managed to solve this by introducing a conditional statement into views.py (note, context statements not included below):
if request.method == "POST":
if form.is_valid():
#do stuff
return HttpResponseRedirect(reverse('your_named_url'))
elif request.method =="GET":
return render(request, 'path/to/template.html', context)
what this does is allows the page to reload with the new content if it's updated (so you can use the form.imagefile.value attribute mentioned previously)
! this is quite a specific use case, but i hope it helps someone save a bit of time

Related

How do I pass an additional object to a Django form so I can render some fields in the template?

I have a form in Django where site visitors can submit "gear" to be included in a person's set of gear. Here's the URL to the change form:
# urls.py
path('person/<slug:slug>/gear/submit/', GearSubmitView.as_view(), name='forms/submit_gear'),
You can see that the person for whom the gear is being submitted is represented by a slug in the URL.
Here's the first part of the CreateView:
# views.py
class GearSubmitView(LoginRequiredMixin, CreateView):
"""Allows for third-party submissions for a pro's gear collection."""
template_name = 'forms/submit_gear.html'
form_class = GearSubmitForm
success_message = 'Success: Submission added.'
And the form:
# forms.py
class GearSubmitForm(forms.ModelForm):
class Meta:
model = PersonProduct
fields = ['product', 'version', 'setup_note', 'usage_start_date', 'evidence_link', 'evidence_text']
where PersonProduct is a junction table between my Person and Product models.
And the template:
# submit_gear.html
{% extends '_base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<h2 id="content-header">Submit Gear For {{ person.full_name }}</h2>
{% crispy form %}
</div>
{% endblock content %}
where you can see what I'm trying to do. I want to insert the name of the person represented by the slug in the URL in the form template.
How can I do it?
You can override get_context_data method in your views.py as mentioned in FormMixin.
class GearSubmitView(LoginRequiredMixin, CreateView):
"""Allows for third-party submissions for a pro's gear collection."""
template_name = 'forms/submit_gear.html'
form_class = GearSubmitForm
success_message = 'Success: Submission added.'
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
person = Person.objects.filter(slug=self.kwargs.get("slug")).first()
data['full_name'] = person.full_name if person else ""
return data
You can change the variable name with full_name in the html file. You can also pass whole instance if you need, I just minimize the data sending from view to html. I didn't run the code block above but it should something like this.
Here you can use the get_context_data method inside of your createview it will look like :
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['user'] = self.request.user
return context
and on your template you will add :
{{user}}
Hope it help you out !

Django - How do I add a placeholder on a modelform that's the current field of the model?

I want to be able to vary the placeholder like so:
<input placeholder=" {{ input.placeholder }}">
Where input is a model with the "placeholder" field. The placeholder field will vary since I'll be using a formset, and each placeholder will vary.
Here's my modelForm
class Value(forms.ModelForm):
class Meta:
model = ValueModel
fields = ['value_text']
widgets = {
'value_text': forms.TextInput(attrs={'class': 'form-control'})
and my modelformset
values_formset = modelformset_factory(model=ValueModel, extra=0, form=Value)
I've tried
class Value(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(Value, self).__init__(*args, **kwargs)
self.fields['value_text'].widget.attrs['placeholder'] = self.fields['placeholder']
class Meta:
model = ValueModel
fields = ['value_text', 'placeholder']
widgets = {
'value_text': forms.TextInput(attrs={'class': 'form-control'})
And other attempts at trying to modify the self.fields with no success.
Edit: The relevant part of my views.py:
def page_view(request, values_id):
values_form = values_formset(queryset=ValueModel.objects.filter(
values_id=values_id))
context = {'value': values_form}
return render(request, 'view.html', context)
My template view:
{{ value.management_form }}
{% for form in value %}
{{ form.id }}
{{ form.value_text }}
{% endfor %}
self.fields['placeholder'] refers to a form field object, not a value; you couldn't use it as a placeholder. But it seems like what you want is to use the value of the model instance.
def __init__(self, *args, **kwargs):
super(Value, self).__init__(*args, **kwargs)
self.fields['value_text'].widget.attrs['placeholder'] = self.instance.placeholder

csrfmiddlewaretoken error while creating form using cripsy-forms

I am beginner in django (using django 1.7) and trying to create a form using crispy-forms in order to add new product to db. The problem is, form is working but it is not creating new product in database.
when I logged, if i click to save button nothing happen and shows below in address bar.
http://127.0.0.1:8000/add_product/?csrfmiddlewaretoken=kfGpEA6ZC32Lad9m9uwWZEhElwBGLHPA&csrfmiddlewaretoken=kfGpEA6ZC32Lad9m9uwWZEhElwBGLHPA&Category_IDCategory=66&DealType=Rent&Title=kkjkj&Price=78&Description=kjjk&save=Save
if I logged out and click to save button it directs me to the homepage as I provide in form but still no new product in database.
The problem looks like related to user and csrf, but still couldnt figure out the exact problem even I searched need your help.
models.py
class Product(models.Model):
DealType_Choice = (
("Sale", "Sale"),
("Rent", "Rent"),
("Swap", "Swap"),
("Free", "Free"),
("Announ", "Announ"),
)
DealType = models.CharField(max_length=11, blank=True, choices=DealType_Choice)
Title = models.CharField(max_length=70)
Description = models.TextField(blank=False)
Price = models.IntegerField(max_length=11, null=True)
User_IDUser = models.ForeignKey(User)
Category_IDCategory = models.ForeignKey(Category)
PubDate = models.DateField("Publication Data")
def __str__(self):
return self.Title
views.py
def add_product(request):
product_form= ProductForm(request.POST)
if product_form.is_valid():
form=product_form.save(commit=False)
form.User_IDUser= request.user
form.save()
return HttpResponseRedirect('/')
else:
product_form= ProductForm()
return render(request, 'add_productts.html', {'product_form':product_form})
forms.py
class ProductForm(forms.ModelForm):
Category_IDCategory=forms.ModelChoiceField(queryset=Category.objects.all(), label="Category")
DealType=forms.ChoiceField(widget=forms.Select, choices=Product.DealType_Choice, label="DealType")
Title=forms.CharField(label='Title', max_length=70)
Price=forms.IntegerField(min_value=0, label='Price')
Description=forms.CharField(widget=forms.Textarea(), label="Description")
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_method = "POST"
self.helper.form_action= "/"
self.helper.layout= Layout(
Field('Category_IDCategory',css_class='input-sm'),
Field('DealType',css_class='input-sm'),
Field('Title',css_class='input-sm'),
Field(PrependedText('Price', 'TL', '.00'),css_class='input-sm'),
Field('Description',css_class='input-sm', rows=5),
FormActions(
Submit('save', 'Save', css_class='btn btn-labeled btn-info'))
)
super(ProductForm, self).__init__(*args, **kwargs)
class Meta:
model=Product
fields= ['Category_IDCategory','DealType', 'Title','Price', 'Description']
template
{% extends "index.html" %}
{% load crispy_forms_tags %}
{% block content %}
<h1>Add Product</h1>{% csrf_token %}
{% crispy product_form %}
{% endblock %}
urls.py
urlpatterns = patterns('',
...
url(r'^add_product/$', add_product),
...
)
I think this is wrong:
self.helper.form_action= "/"
form_action should refer to add_product function:
self.helper.form_action= "/add_product/"

MultiValueDictKeyError in django modelformset_factory

I'm trying to imeplement an edit formset. Then, i'm instancing the objects in the formset using modelformset_factory. When the request isn't POST, the formset loads perfectly, but, if the request is POST, the formset constructor raises a MultiValueDictKeyError.
This is my code.
forms.py
class SchoolSectionForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
Div(
'name',
css_class='name',
),
)
super(SchoolSectionForm, self).__init__(*args, **kwargs)
class Meta:
model = SchoolSection
exclude = [
'school',
'created_by',
]
class SectionBreakFormSet(BaseFormSet):
def __init__(self, *args, **kwargs):
super(SectionBreakFormSet, self).__init__(*args, **kwargs)
class SectionBreakForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
Div(
Div(
'start_time',
css_class='start_time',
),
Div(
'end_time',
css_class='end_time',
),
css_class='formset_element',
)
)
super(SectionBreakForm, self).__init__(*args, **kwargs)
class Meta:
model = SectionBreak
exclude = [
'school_section',
'created_by',
]
views.py
def school_section_edit(request, section_id):
section = get_object_or_404(
SchoolSection,
id=section_id,
)
current_school = request.user.current_school
school_sections_list = current_school.schoolsection_set.all()
section_break_formset = modelformset_factory(
SectionBreak,
max_num=3,
extra=0,
form=SectionBreakForm,
)
formset_qset = SectionBreak.objects.filter(school_section=section)
formset = section_break_formset(queryset=formset_qset)
school_section_form = SchoolSectionForm(instance=section)
if request.method == 'POST':
school_section_form = SchoolSectionForm(request.POST)
# Bug raises in this line
formset = section_break_formset(request.POST, queryset=formset_qset)
# Bug raises in this line
if school_section_form.is_valid() and formset.is_valid():
school_section_form.save()
formset.save()
messages.success(
request,
u'xxx',
)
return HttpResponseRedirect(reverse('school:school_section_add'))
else:
messages.error(
request,
u'xxx',
)
return render(request, 'school/schoolsection_add.html', {
'school_section_form': school_section_form,
'formset': formset,
'school_sections_list': school_sections_list,
})
template
<form class="new_section_form" method="post" action="">
<div class="school_section_form">
{% crispy school_section_form %}
</div>
<h3>Horarios de descanso:</h3>
<div class="section_break_formset">
{% crispy formset formset.form.helper %}
</div>
<button class="button color">guardar</button>
</form>
When i post the form... crashh.... i have this error
thanks for help...
Exception Type: MultiValueDictKeyError at /administrador/ciclo-educativo/editar/34/
Exception Value: "Key u'form-0-id' not found in <>QueryDict: {u'name': [u'Primaria'], u'form-MAX_NUM_FORMS': [u'3'], u'form-TOTAL_FORMS': [u'1'], u'form-0-start_time': [u'07:00:00'], u'form-0-end_time': [u'12:00:00'], u'form-INITIAL_FORMS': [u'1'], u'csrfmiddlewaretoken': [u'aZkZPJ6tlzJeCd1kjC0EpkjPuFbWe6IB', u'aZkZPJ6tlzJeCd1kjC0EpkjPuFbWe6IB']}<>"
You might need to add the form id {{ form.id }} e.g.
{% crispy formset formset.form.id %}
Form id is hidden field but if you not include it in form in your template then it will through the above error.
if include it in your template you will get rid of above error
Ex:
{{form.title}} #this field you want to display
{{ form.id }} # this is hidden field, this will not get display in form. but at the time of form-set submission this is required.

Django Forms: pass parameter to form

How do I pass a parameter to my form?
someView()..
form = StylesForm(data_dict) # I also want to pass in site_id here.
class StylesForm(forms.Form):
# I want access to site_id here
You should define the __init__ method of your form, like that:
class StylesForm(forms.Form):
def __init__(self,*args,**kwargs):
self.site_id = kwargs.pop('site_id')
super(StylesForm,self).__init__(*args,**kwargs)
of course you cannot access self.site_id until the object has been created, so the line:
height = forms.CharField(widget=forms.TextInput(attrs={'size':site_id}))
makes no sense. You have to add the attribute to the widget after the form has been created. Try something like this:
class StylesForm(forms.Form):
def __init__(self,*args,**kwargs):
self.site_id = kwargs.pop('site_id')
super(StylesForm,self).__init__(*args,**kwargs)
self.fields['height'].widget = forms.TextInput(attrs={'size':site_id})
height = forms.CharField()
(not tested)
This is what worked for me. I was trying to make a custom form . This field in the model is a charfield but I wanted a choice field generated dynamically .
The Form:
class AddRatingForRound(forms.ModelForm):
def __init__(self, round_list, *args, **kwargs):
super(AddRatingForRound, self).__init__(*args, **kwargs)
self.fields['name'] = forms.ChoiceField(choices=tuple([(name, name) for name in round_list]))
class Meta:
model = models.RatingSheet
fields = ('name', )
The Views:
interview = Interview.objects.get(pk=interview_pk)
all_rounds = interview.round_set.order_by('created_at')
all_round_names = [rnd.name for rnd in all_rounds]
form = forms.AddRatingForRound(all_round_names)
return render(request, 'add_rating.html', {'form': form, 'interview': interview, 'rounds': all_rounds})
The Template:
<form method="post">
{% csrf_token %}
{% if interview %}
{{ interview }}
{% if rounds %}
{{ form.as_p }}
<input type="submit" value="Submit" />
{% else %}
<h3>No rounds found</h3>
{% endif %}
</form>
someView()..
form = StylesForm( 1, request.POST)
in forms.py
class StylesForm(forms.Form):
#overwrite __init__
def __init__(self,site_id,*args,**kwargs):
# call standard __init__
super().__init__(*args,**kwargs)
#extend __init__
self.fields['height'] =forms.CharField(widget=forms.TextInput(
attrs= {'size':site_id}))
height = forms.CharField()
or
someView()..
form = StylesForm(site_id = 1)
in forms.py
class StylesForm(forms.Form):
#overwrite __init__
def __init__(self,site_id):
# call standard __init__
super().__init__()
#extend __init__
self.fields['height'] =forms.CharField(widget=forms.TextInput(
attrs= {'size':site_id}))
height = forms.CharField()