Why my Model Form does not pass the validation - python

I am writing simple blog in django and I gotstuck on uploading files.
I want to create template wher user can write post and add multiple files. So firstrly i have created models:
class Post(models.Model):
title= models.CharField(verbose_name=u"post title", max_length=40, blank=False)
body= models.TextField(verbose_name=u"post body", blank=False)
author= models.ForeignKey(User, blank=False)
published= models.DateTimeField(verbose_name=u"data publikacji", blank=True, null=True)
class Add(models.Model):
post=models.ForeignKey(Post)
file=models.FileField(upload_to='adds', verbose_name=u"added files")
and after that i created template which looks like this:
{% block content %}
<div class="col-lg-6 col-md-offset-3">
<form enctype="multipart/form-data" method="post" adction="{% url 'create_post' %}">{% csrf_token %}
{{post_form|crispy}}
<input type="file" name="files" multiple/></br>
<input class="btn btn-success" style="width:100%" type="submit" value="Dodaj post"/>
</form>
</div>
{% endblock %}
after that i wrote simple view which would handle files and post creation:
def create_post(request):
if request.method== 'GET':
post_form=PostForm()
add_form=AddForm()
context={
'post_form': post_form,
}
return render(request, 'create_post.html', context)
else:
post_form=PostForm(request.POST)
if post_form.is_valid():
instance=post_form.save(commit=False)
instance.published=datetime.datetime.now()
instance.author=request.user
instance.save()
# import ipdb
# ipdb.set_trace()
for file in request.FILES.getlist("files"):
add_instance=AddForm()
add_instance.post=instance
add_instance.file=file
if add_instance.is_valid():
add_instance.save()
return redirect(reverse('AllPosts'))
but none of add_instance passes the validation "is_valid". But why?
And the second question is there any way to use Form in template against writing raw input for file? When i use AddForm i can only add one file what is logic.

You problem is that your form is always an unbounded form, which means that it is initialized without data. By data I mean POST or GET data from request or an instance of the model. unbounded form will always fail is_valid() and it doesn't make sense to manually assign anything to a form instance like add_instance = AddForm() then add_instance.post = instance.
django doc explains how to use a form.
You cannot upload multiple files with one single form because each form is a Add model form. You should really use ModelFormSet for your AddForm to add multiple model instances in one submit.
django doc explains how to use ModelFormSet.
By the way, I find the model name Add extremely confusing, consider changing it to Attachment or PostFile or something else.
Edit:
I didn't try it, but if you want one widget for multiple files, you can do this in views:
for upload_file in request.FILES.getlist('files'):
form = AddForm(request.POST, {'file': upload_file})
if form.is_valid():
new_file = form.save(commit=False)
new_file.post = instance
new_file.save()

Related

Pass data from one form to another one in Django forms/views

I'm not too knowledgeable in Django, but I wanted to implement this idea that I have but I can't seem to think how to get there. Now, on my web app, there is a form with a field called "contractor", and it has a button which, when clicked, opens a modal with another form with different fields about the "contractor". I would like it to, when information is submitted about the contractor in the second form, it autofills the field in the first form with the data from the first field of the second form. Also, I want to make it so that when the user starts typing in that "contractor" field in the first form, already submitted data can be chosen. I leave my code here to give you an idea:
models.py
class Contratos(models.Model):
name=models.CharField(max_length=30)
contractor=models.ForeignKey(Vendors, on_delete=models.CASCADE, to_field='contractee_vendor')
contractee=models.CharField(max_length=30)
[......]
class Vendors(models.Model):
contractee_vendor=models.CharField(max_length=30, unique=True)
company=models.CharField(max_length=30)
description=models.TextField()
[......]
views.py
def ContractView(request):
form=contractsform(request.POST)
if form.is_valid():
con =form.save()
return redirect('contracts', con.id)
else:
form = contractsform()
return render(request, 'contform.html', {'form': form})
class VendorsModal(BSModalCreateView):
template_name = 'vendform.html'
form_class = vendorsform
success_message = 'Success: Vendor created'
success_url = reverse_lazy('new-contract')
from my contracts HTML to open modal
<form action="" method=POST>
{% csrf_token %}
<label for="{{ form.subject.id_for_label }}">Name:</label>
{{form.name}}
<label for="{{ form.subject.id_for_label }}">Contractor:</label>
<div class="input-group mb-3">
{{form.contractor}}
<button id="new-vendor" class="btn btn-outline-secondary" type="button">+</button>
</div>
I don't know if there is a simple way to do this with Django, or if I have to checkout JavaScript, but something tells me there is a simple way to do this with Django.

How to save form data to my model in django

I am creating a newsletter application that requires the user's name and email. However each time I input form data . no change is reflected in the database
models.py
class NewUsers(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField()
date_added = models.DateField(auto_now_add= True)
class Meta:
verbose_name = "NewUser"
verbose_name_plural = "NewUsers"
def __str__(seld):
return self.email
views.py
def newsletter_subscribe(request):
if request.method == 'POST' :
form = NewUserForm(request.POST)
if form.is_valid():
name = form.cleaned_data['name'] #variable to store cleaned data
email = form.cleaned_data['email']
instance = NewUsers(name= name, email = email)
instance.save()
if NewUsers.objects.filter(email = instance.email).exists():
print("Your email is already added to our database")
else:
instance.save()
print("Thank you for subscribing")
else:
form = NewUserForm()#display empty form
context = {'form':form}
template = "index.html"
return render(request ,template ,context )
Here is my template code
template
<form method="post" action="{%url 'subscribe'%}">
{% csrf_token %}
<label for="id_email_field">Name:</label> <input type="text"
name=""
required="" id="">
<label for="id_email_field">E-mail:</label> <input type="email"
name="email_field"
required="" id="id_email_field">
<button id="id_submit" name="submit" value="Subscribe"
type="submit">Subscribe
</button>
</form>
A few things I notice: First, a view must return an HttpResponse object. E.g., I recommend reading up here: https://docs.djangoproject.com/en/2.1/topics/http/views/ and here: https://docs.djangoproject.com/en/2.1/topics/forms/
So, since apparently you didnt get an error thrown at you pointing to this fact, I assume that the request.method never has been equal "POST". Maybe you could try to find out if this is the case? Therefore: could you also provide your template code, please.
Next, your code in the if form.is_valid() is quite contrived. The most natural thing to do here is just calling form.save(). This will create an instance in your db out of the cleaned form-data. In case, you need to do some adjustments, you can extend like this:
instance = form.save(commit=False)
# add some adjustments (instance.foo = bar)
instance.save()
Last, as noted before, you need to return an HttpResponse object which is usually done via
return redirect(url_name, ..)
Edit: since you now added the template code: Try to first let django render the fields for you: https://docs.djangoproject.com/en/2.1/topics/forms/#rendering-fields-manually
and then have a look at the source code of the template. Your name-input-field is missing a name tag and your email-input-field should have name="email" I think. You can django even let the whole form render for you (see docs again...) Without (correct) name tags in the input fields - it will not be possible to send or correctly assign the data inputted by the user.

Passing request.POST vs the request itself to a django form when uploading multiple files

I am using following form to upload multiple images.
class AlbumForm(forms.Form):
album_name = forms.CharField(label='Titel', max_length=100, required=True)
album_description = forms.CharField(label='Beschrijving', widget=forms.Textarea)
images = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}))
In the receiving view i noticed that when i pass request.POST to the form I cannot access the images in the form. On the other hand when i pass the request itself to the form i can access the files but cannot use the form.isValid() method.
Only the multiple image file is not available, the two other values are available as usual.
def post(self, request, *args, **kwargs):
images_form = AlbumForm(request)
...
if images_form.is_valid():
images = images_form.data.FILES.getlist('images')
Imgur().create_album(title=images_form.album_name, description=images_form.album_description, images=images)
...
Following is the html code for the form
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{images_form|crispy}}
<input class="" type="submit" value="save">
</form>
What is the reason for this difference or what am i doing wrong?
Edit : the name and description are not available when only using the request, so i guess the correct way to go is to use request.POST, but what is then the correct way to acces the files

Django form missing fields

I'm trying to get a simple form working. Oddly, other forms I wrote in this app are working fine, but this one wont show the fields. Can anyone tell me what I'm missing? Here are the files
views.py:
def newnote(request, record_id):
if request.method == 'POST':
form = NoteForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/tracker/all/')
else:
form = NoteForm()
return render(request, 'tracker/noteform.html', {'form': form})
models.py
class Note(models.Model):
record = models.ForeignKey(Record, on_delete=models.CASCADE)
note_text = models.CharField('Notes', max_length=2000)
note_date = models.DateField('Date Entered')
forms.py
class NoteForm(forms.Form):
class Meta:
model = Note
fields = ['note_text',
'note_date'
]
template (noteform.html)
<form action="/tracker/newnote/" method="post">
<div id="fields">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</div>
</form>
One other note, I have commented out the div id called "fields", to rule out CSS as the issue.
Your form is based on form.Form, which doesn't know anything about models, doesn't expect a Meta class, and expects all its fields to be declared manually - since you have not declared any fields, nothing will show on the template.
It should inherit forms.ModelForm instead.

Attaching a current User object to Django form

I am working on an app that has a section with with a file upload form for .txt fiels. I would like for the current user that is uploading the file to be added along with the file and the file name. Currently, I can do this successfully in the admin section but I just cant get it to save via the form itself. Any Ideas?
Here are the models:
class UploadedTextFile(models.Model):
file = models.FileField(upload_to="textfiles")
filename = models.CharField(max_length = 50)
username = models.ForeignKey(User, blank=True, null=True)
class UploadedTextFileForm(ModelForm):
class Meta:
model = UploadedTextFile
fields = ['file', 'filename']
Here is my view:
def inputtest(request):
#response for file being submited
if request.method == "POST":
form = UploadedTextFileForm(request.POST)
if form.is_valid():
new_form = form.save(commit=False)
new_form.username = request.user
new_form.save()
return render(request, 'about.html')
inputtest = UploadedTextFileForm()
return render(request, 'failed.html', {'inputtest': inputtest})
else:
inputtest = UploadedTextFileForm()
return render(request, 'inputtest.html', {'inputtest': inputtest})
Here is my html:
{% extends 'base.html' %}
{% block content %}
<form method="post">{% csrf_token %}
{{ inputtest.as_p }}
<input type="submit" value="Submit" />
</form>
{% endblock content %}
Doing it in the view (as you've shown) is the right way to do this. Most likely you're having problems because you've left username as a field on the form, and because the FK model field doesn't have blank=True set the form requires the field to be provided. You should explicitly declare just the subset fields that you want to accept user input for in the form's Meta class.
class UploadedTextFileForm(ModelForm):
class Meta:
model = UploadedTextFile
fields = ['file', 'filename']
I am not sure why you're rendering a different template when the form is not valid, but no matter what you're not providing the form object in the context. This means that you'll never see any errors the form detects, which is probably what's happening with this code - you're not seeing the error that username is not provided.

Categories

Resources