I am writing a simple view using Django Model Form, however image field fails to validate with 'This field is required' error message.
I wonder where the problem is...
Model:
class Deal(AbstractModel):
IMAGE_MAX_LENGTH = 200
place = models.ForeignKey(Place, related_name='deals', related_query_name='deal', verbose_name=_("Place"))
image = models.ImageField(default='deals/default.png', max_length=IMAGE_MAX_LENGTH, upload_to='deals', verbose_name=_("Image"))
...
View:
#login_required
def deals_create(request):
# Get place id
place_id = request.GET.get('place')
# Get place
place = Place.objects.get(id=place_id)
# Process form data
if request.method == 'POST':
form = DealsCreateForm(request.POST, request.FILES)
# Validate post data
if form.is_valid():
# Save deal data
deal = form.save(commit=False)
deal.place = place
deal.save()
# Redirect to reload page and clear post data
return HttpResponseRedirect(reverse('deal_manager:deals_pending'))
else:
form = DealsCreateForm()
return render(request, 'deal_manager/deals_create.html', {
'form': form,
'place': place,
})
Form:
class DealsCreateForm(ModelForm):
class Meta:
model = Deal
fields = [
'image', 'product_name', 'product_description',
'regular_price', 'sale_price', 'limit', 'terms',
]
Template:
{% extends "deal_manager/deal_manager.html" %}
{% load i18n %}
{% load staticfiles %}
{% block page_content_body %}
<div class="row">
<div class="span12">
<form action="{% url 'deal_manager:deals_create' %}?place={{ place.id }}" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="{% trans 'Create' %}" />
</form>
</div>
</div>
<div class="gap gap-small"></div>
{% endblock page_content_body %}
Note: Form validates when I remove the image field from form meta fields.
Note: File uploads works fine with Django Admin for this model.
You'll need to include enctype="multipart/form-data" in order to bind imagefield and filefield data to the form. Without that, those fields won't validate.
{% block page_content_body %}
<div class="row">
<div class="span12">
<form enctype="multipart/form-data" action="{% url 'deal_manager:deals_create' %}?place={{ place.id }}" method="post">
...
Here's the relevant documentation: https://docs.djangoproject.com/en/stable/ref/forms/api/#binding-uploaded-files-to-a-form
Related
HI I have just built an HTML-form in which the user can upload multiple images but I am getting Method Not Allowed (POST): /images/ all the time. In the image_list.html the images should be shown.
Thanks for your help
models.py
class ImageModel(models.Model):
images = models.ImageField(upload_to='products/')
forms.py
class ImageForm(ModelForm):
class Meta:
model = ImageModel
fields = ['images']
widgets = {
'images': FileInput(attrs={'multiple': True}),
}
views.py
class ImageFormView(FormMixin, TemplateView):
template_name = 'image_form.html'
form_class = ImageForm
def form_valid(self, form):
# Save the images to the database
form.save()
# Redirect to the image list view
return redirect('image_list')
class ImageListView(ListView):
model = ImageModel
template_name = 'image_list.html'
context_object_name = 'images'
image_form.html
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Submit
</form>
image_list.html
{% load static %}
{% for image in images %}
<img src="{{image.images.url}}" alt="">
{% endfor %}
urls.py
urlpatterns = [
path('images/', views.ImageFormView.as_view(), name='image_form'),
path('', views.ImageListView.as_view(), name='image_list'),
]
You need to close button tag at HTML form
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
I can't post in Django, because when I import an image it doesn't work for me. it tells me that there's no file selected but I selected one.
This is the post model that I created, models.py file:
class Post(models.Model):
publisher = models.ForeignKey(User,on_delete=models.CASCADE)
caption = models.CharField(max_length=100)
date_created = models.DateTimeField(default=timezone.now())
image = models.ImageField(upload_to="post_images")
def __str__(self):
return self.caption
here's the forms.py file for the Post model:
from django import forms
from .models import Post
class CreatePostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['caption','image']
here's the Publish function in views.py file which implements the logic for my publish feature:
#login_required
def Publish(request):
if request.method == "POST":
form = CreatePostForm(request.POST,request.FILES)
if form.is_valid():
form.publisher = request.user
form.save()
return redirect("home")
else:
form = CreatePostForm()
return render(request,"posts/publish.html",{
"form":form,
})
int the urls.py file:
from django.urls import path
from . import views
urlpatterns = [
path('publish/',views.Publish,name="publish"),
path('',views.home,name="home"),
]
and here's in html template:
{% extends "users/base.html" %}
{% load crispy_forms_tags %}
{% block title %}create{% endblock title%}
{% block content %}
<div class="container-fluid">
<div class="row">
<div class="col-sm-6 col-md-5 authentification">
<div class="form-header">
<h1>
publish
</h1>
</div>
<div class="form-body">
<form method="POST">
<fieldset class="form-group" enctype="multipart/form-data">
{% csrf_token %}
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button type="submit" class="btn btn-primary form-control">publish</button>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock content %}
the Django version used is 2.2 and the Python 3.8. and Windows 10 Pro
You should alter the .publisher attribute of the .instance wrapped in the form, not the form itself, so:
#login_required
def Publish(request):
if request.method == 'POST':
form = CreatePostForm(request.POST,request.FILES)
if form.is_valid():
form.instance.publisher = request.user
form.save()
return redirect('home')
else:
form = CreatePostForm()
return render(request,'posts/publish.html',{
'form': form,
})
Since you are submitting both files and data, you should specify the enctype=… attribute [mdn] in the <form>:
<form enctype="multipart/form-data" method="POST">
…
</form>
Note: Django's DateTimeField [Django-doc]
has a auto_now_add=… parameter [Django-doc]
to work with timestamps. This will automatically assign the current datetime
when creating the object, and mark it as non-editable (editable=False), such
that it does not appear in ModelForms by default.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
I am creating one web system and need to get one value especific from ModelForm for store in another system, in addition to storing in django. Example: get field "nome" from Model Form for to store in LDAP server
model.py
class Orientado(models.Model):
nome = models.CharField(max_length=100,)
telefone = models.CharField(max_length=20)
email = models.EmailField(max_length=100)
forms.py
class OrientadoForm(forms.ModelForm):
class Meta:
model = Orientado
fields = ['nome', 'telefone','email']
views.py
def person_new(request):
form = OrientadoForm(request.POST or None, request.FILES or None)
autor = User.objects.get(username=request.user.username)
if form.is_valid():
form.save()
return redirect('person_list')
return render(request, 'person_form.html',{'form': form, 'autor': autor})
html
{% block main %}
<h3>Novo Orientado</h3>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.non_field_errors }}
{{ form.source.errors }}
{{ form.source }}
{{ form|bootstrap }}
<button type="submit" class="btn btn-primary">Salvar</button>
</form>
{% endblock %}
I want to show custom error messages, if some field is not valid. I have following model:
class Test(models.Model):
name = models.IntegerField(max_length=10)
class TestForm(forms.ModelForm):
class Meta:
model = Test
fields = '__all__'
error_messages = {
'name': {
'max_length': ("This user's name is too long."),
},
}
The view is:
def test(request):
if request.method == 'POST':
print "The form is submitted successfully."
form = TestForm(request.POST)
if form.is_valid():
print request.POST.get("name")
return render(request, 'test.html',{'form' : TestForm()})
else:
print "Something wrong with inputs."
return render(request, 'test.html',{'form' : form})
else:
return render(request,'test.html',{'form' : TestForm()})
and template is:
{% extends "base.html" %}
{% block title %}
Test Form
{% endblock title %}
{% load widget_tweaks %}
{% block body_block %}
<h1>hello from test</h1>
<form class='form-horizontal' role='form' action="." method="POST">
<div class='form-group'>
<label class='control-label col-md-2 col-md-offset-2' for='id_name'>Name</label>
<div class='col-md-6'>
{% render_field form.name class="form-control" placeholder="Full Name" type="text" %}
{{ form.name.error_messages }}
{# I want to add here classes for alert-error etc #}
</div>
</div>
{% csrf_token %}
<div class='form-group'>
<div class='col-md-offset-4 col-md-6'>
<button type="submit" class="btn btn-success">Submit</button>
</div>
</div>
</form>
{% endblock body_block %}
But, I am not getting any messages in the template. Please help me to solve this.
Change form.name.error_messages to form.name.errors in your template.
It seems you render fields/errors one by one manually, explained here:
You may want to consider a more automatic approach using a {% for %} template tag.
EDIT: To change the default error message, you need to update your error_messages in the form Meta and overwrite the key used by django, in this case it is key invalid, based on IntegerField source:
class Meta:
model = Test
fields = '__all__'
error_messages = {
'some_integer_field': {
'invalid': 'some custom invalid message',
},
}
I've been trying to figure out how to create a custom html template that when submitted will upload to a model form. I'm newer to Django so so far i've been a little confused on the Django Docs on the forms. I have created a custom HTML Template it looks like:
HTML:
<form role="form" method="post" action="." id="js-upload-form" enctype="multipart/form-data">
{% csrf_token %}
<img id="image1" src="/media/{{ gallery.logo }}" alt="">
<input type="file" name="logo" id="logo" multiple>
<br>
<input type="submit" value="Register" id="js-upload-submit" >
</form>
You can notice that I have given the input ID = logo. When I click submit I would like this to upload an image to my ModelForm.
Form:
class UploadFileForm(ModelForm):
logo = forms.ImageField(required=False)
class Meta:
model = Content
fields = ['logo']
Models:
class Content(models.Model):
logo = models.ImageField(upload_to=content_file_name, null=True, blank=True)
Is there anyway with the specific way I have designed my HTML template to submit the image to the Model Form? I am trying to avoid using { form.as_p } and such as it doesnt do the customization I would like.
You should send the form object to the template and call the {{form.logo}} field.
View:
if request.method == 'POST': # When the form is submitted
form = UploadFileForm(request.POST)
if form.is_valid():
new_content = form.save()
# After the form.save a new model is created
# and the file is uploaded to the desired location
else:
form = UploadFileForm()
ctx = {}
ctx['form'] = form
return render_to_response('yourtemplate.html', ctx, context_instance=RequestContext(request))
Template:
<form role="form" method="post" action="." id="js-upload-form" enctype="multipart/form-data">
{% csrf_token %}
{{form.logo}}
</form>
Customization:
If you would like to customize the input field, you should hide the form field like:
<form role="form" method="post" action="." id="js-upload-form" enctype="multipart/form-data">
{% csrf_token %}
{{form.logo.as_hidden}} # This won't show the input
</form>
And now to customize the input you should show your custom input and via jQuery or JavaScript bind the custom input/button to the hidden form logo field.
E.g:
If you want to trigger the file select with a custom button, you should do:
# ... Other code
<button class='btn btn-success yourclass' onClick='selectFile'>
<script>
function selectFile(){
$('#id_logo').click()
}
<script>