ImageField doesnt work - python

When i try to upload file it gives me template error, that its required to fill in. Code:
models:
class ahoja(models.Model):
image = models.ImageField(upload_to='smayat')
forms:
class ahojaForm(ModelForm):
class Meta:
model = ahoja
exclude = ()
view:
def testview(request):
if request.method == 'POST': # pokud form byl odeslan
form = ahojaForm(request.POST, request.FILES) # formular s daty
if form.is_valid():
form.save() #vytvoří událost
return HttpResponseRedirect('/hlavni_stranka/kalendar/')
else:
form = ahojaForm() # prázdný formulář
return render(request, 'hlavni_stranka/test.html', {'form': form,})

The first thing to check is the enctype attribute in your template. From the docs:
Note that request.FILES will only contain data if the request method was POST and the <form> that posted the request has the attribute enctype="multipart/form-data". Otherwise, request.FILES will be empty.

Related

Cannot add model instance to OneToManyField in django - "None has no attribute add"

I already have seen this bug in other post, but still in trouble.
I'm trying to create a social network like instagram where users will be able to publish posts (photos).
I have User class which herit from AbstractUser, and got a OneToMany field of posts: each user can publish many posts.
After successfully pulling my photo from: PostForm(request.POST, request.FILES) and saving it correctly, I cannot add this photo to the current user's publications/posts and got error:
'NoneType' object has no attribute 'add'
def blog_and_photo_upload(request):
form = PostForm()
if request.method == 'POST':
form = PostForm(request.POST, request.FILES)
if form.is_valid():
user = get_user(request) # user instance is correct with good pk
post = Post.objects.create(image=form.cleaned_data['image']) # post instance looks correct also
post.save()
user.save()
user.posts.add(post) # row doesnt work
redirect('home')
return render(request, 'base/upload_post.html', {'form': form})
models.py
class Post(models.Model):
...
image = ResizedImageField(size=[300, 300], blank=True, upload_to='posts')
class User(AbstractUser):
...
posts = models.ForeignKey(Post, on_delete=models.Cascade, null=True)
You can simply update the form like this:
post = Post.objects.create(image=form.cleaned_data['image']) # post instance looks correct also
post.save()
user.posts = post
user.save()
return redirect('home')
But, I think the design of the model is wrong, User to Post relation should be like this:
Class User(...):
posts = models.ManyToManyField(Post)
In that way, your original implementation should work. (Probably you don't need user.save() call in your view).
At first there should be return redirect(...) not only redirect() and secondly try to use the following view:
def blog_and_photo_upload(request):
form = PostForm()
if request.method == 'POST':
form = PostForm(request.POST, request.FILES)
if form.is_valid():
user = get_user(request) # user instance is correct with good pk
post = Post.objects.create(image=form.cleaned_data['image']) # post instance looks correct also
post.save()
user.posts.add(post) # add post to user's posts field
user.save()
return redirect('home')
return render(request, 'base/upload_post.html', {'form': form})
You need to bind first Post with User model like add a ForeignKey or a ManyToManyFields to relate them
posts = models.ForeignKey(User)
then you will be able to call it like you did
user.posts # this won't return None
Check this many to many field docs: https://docs.djangoproject.com/en/4.1/topics/db/examples/many_to_many/

Django add primary key before saving new object

I'm new to Django and have a Q&A project. For each question you may have multiple tags which already exist or put new ones. The new tags should be created before the question is saved. How do I solve this properly? So far I have:
def question_add(request):
# redirect user to login page if not authenticated
if not request.user.is_authenticated():
return render(request, 'account/login.html')
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = QuestionForm(request.POST)
if form.is_valid():
# process the data in form.cleaned_data as required
instance = form.save(commit=False)
instance.created_by = request.user
instance.save()
messages.success(request, 'Question added with success.')
# redirect to the main page:
return HttpResponseRedirect('/')
else:
messages.warning(request, 'Please correct the errors.')
# if a GET (or any other method) we'll create a blank form
else:
form = QuestionForm()
return render(request, 'question/add.html', {'form': form})
This should be done prior form.is_valid() or does it exist a magic method for doing this?
I am assuming tag is ManyToMany field for Question model.
Inside form.is_valid() to add manyToMany field data.
if form.is_valid():
instance = form.save(commit=False)
instance.created_by = request.user
instance.save()
# returns the list of tag names
tags = request.POST.get('tags')
for tag in tags:
# create if tag not found with given name or return existing tag
obj, created = Tag.objects.get_or_create(name=tag)
instance.tags.add(obj)
If the Tag is ForiegnKey:
if form.is_valid():
instance = form.save(commit=False)
instance.created_by = request.user
tag_name = request.POST.get('tag')
obj, created = Tag.objects.get_or_create(name=tag_name)
instance.tag = obj.id
instance.save()

HttpResponse won't display `post` information when submitting a form

I'm working my way through Django and I'm creating an app that will allow users to use an ID number to sign-in to a system. So I have two views, one for users to log-in, and the other to sign-up. The former view works just fine, I can get it to display the information the user has submitted. However I can't get the second view to display the POST data to the user:
from .forms import NameForm, IdForm
from django.shortcuts import render
from django.http import HttpResponse
def sign_in(request):
if request.method == "POST":
#here will construct the form with the POST data
form = NameForm(request.POST)
#the next part is to check that the information submitted is valid
if form.is_valid():
post = form.save()
post.save()
return HttpResponse(post)
else:
return HttpResponse("Form is invalid")
else:
form = NameForm()
return render(request, 'checkin/base.html', {'form': form})
def sign_up(request):
if request.method == "POST":
form = IdForm(request.POST)
if form.is_valid():
post = form.save()
post.save()
return HttpResponse(post)
else:
return HttpResponse('Form is invalid')
else:
form = IdForm()
return render(request, 'checkin/base.html', {'form': form})
Basically I want to make the response to be "thank you, your ID number is: post".
Here is the class for my model:
from __future__ import unicode_literals
from django.db import models
from django import forms
from django.forms import ModelForm
# Create your models here.
class Question(models.Model):
question_text = models.CharField("What is your ID?", max_length=200)
id_text = models.CharField("Enter a new identification
number",max_length=200, null=True)
def __str__(self):
return self.question_text
And here are the form classes for both views:
from django.forms import ModelForm
from .models import Question
#put the form here
class NameForm(ModelForm):
class Meta:
model = Question
fields = ['question_text']
class IdForm(ModelForm):
class Meta:
model = Question
fields = ['id_text']
It's not generally acceptable to display the POST data as the respnose to the user. That's not HTML, merely a dictionary which the average user will not understand. The standard way of using forms in Django (and indeed almost any web framework) is to display the form validation errors to the user so that he may rectify it.
The right way
def sign_up(request):
if request.method == "POST":
form = IdForm(request.POST)
if form.is_valid():
post = form.save()
post.save()
return HttpResponseRedirect('/succes_url')
else:
form = IdForm()
return render(request, 'checkin/base.html', {'form': form})
The problem is in line return HttpResponse(post),You are passing a whole form into HttpResponse,but as you mentioned,you just need id_text field of the IdForm.
So the updated code should be :
def sign_up(request):
if request.method == "POST":
form = IdForm(request.POST)
if form.is_valid():
post = form.save()
post.save()
id = post.id_text
return HttpResponse('thank you, your ID number is: '+id)
else:
return HttpResponse('Form is invalid')
else:
form = IdForm()
return render(request, 'checkin/base.html', {'form': form})

Convert my def view to class based view

I have this view in my app:
def contact(request):
form_class = ContactForm
if request.method == 'POST':
form = form_class(data=request.POST)
if form.is_valid():
contact_name = form.cleaned_data['contact_name']
contact_email = form.cleaned_data['contact_email']
contact_website = form.cleaned_data['contact_website']
contact_subject = form.cleaned_data['contact_subject']
form_content = form.cleaned_data['content']
template = get_template('contact/contact_template.txt')
context = Context({'contact_name': contact_name,
'contact_email': contact_email,
'contact_website': contact_website,
'contact_subject': contact_subject,
'form_content': form_content, })
content = template.render(context)
email = EmailMessage(
"New contact form submission",
content,
"www.inexistente.com" + '<support#inexistente.com>',
['mymail#gmail.com'],
headers={'Reply-To': contact_email}
)
email.send()
return redirect('/')
return render(request, 'contact/contact.html', {'form': form_class, })
I want to transform this to a class based view, I believe that is "more organized" for me...
What generic Django view to use?
Do you believe that is recommended to do this?
In the view, Is recommended use this code at the end to renew the form in case of not passing?
"else:
form = form_class()"
i was trying without that code and my form renews itself perfectly.
apologizeme in advance if I overlook something, any contribution is wellcome, Thanks for evaluate!
Since you have a view that displays a form and which redisplays the form with validation errors on error and redirects to a new URL on success, you can use FormView generic view.
Your FBV code converted to CBV:
from django.views.generic import FormView
class Contact(FormView):
form_class = ContactForm # Form class to be used
template_name = 'contact/contact.html' # Template to be used
success_url = '/' # Redirect to this url when form is valid
def form_valid(self, form):
template = get_template('contact/contact_template.txt')
context_dict = form.cleaned_data
context_dict['form_content'] = form.cleaned_data['content']
context = Context(context_dict)
content = template.render(context)
email = EmailMessage(
"New contact form submission",
content,
"www.inexistente.com" + '<support#inexistente.com>',
['mymail#gmail.com'],
headers={'Reply-To': contact_email}
)
email.send()
return super(Contact, self).form_valid(form)
Here,
form_class: defines the form class to be used.
template_name: defines the template to be used to display the form.
success_url: defines the url to be used when the form is valid.
You can put all the logic for the code to be executed when form is valid inside the form_valid() function. After performing all the operations, call the super() which redirects to the success_url defined in your class.
Also, when you are building context to be passed to the email template, you can use the form.cleaned_data dictionary. All the keys you have used to build the context dictionary is same as in the form's cleaned_data dictionary except the form_content key. So, i have just used the form's cleaned_data dictionary and added an extra key form_content in a context_dict dictionary which will be then used in the email template for rendering.

django form default to ImageField for rendering

I have the following Django form:
class PageForm(forms.Form):
title = forms.CharField(max_length=50)
image = forms.ImageField(required=False)
content = forms.CharField(widget=forms.Textarea(attrs={'cols':20, 'rows':10}))
I'm using this to create new pages from a given template. But now I want to also add edit possibility, so I would like to use the form to render the same template only with some default values which are retrived from a page id. This is what I have:
page_to_edit = Page.objects.filter(id=page_id)[0] // Get page from model
title = page_to_edit.title
content = page_to_edit.content
picture = page_to_edit.picture.order_by('?')[0].file // Here file is a models.ImageField
initial_data = {'title' : title, 'content' : content, 'image' : picture}
form = PageForm(initial_data)
// Finally return this form to template
Now this works as I want for title and content, and those are properly rendered in template with initial values, but the ImageField is just empty. I've also tried passing picture.url instead of picture but no change.
Any help would be appreciated.
All the best
I would approach this the following way:
You can use .get() to retrieve a single record, Page.objects.get(pk=page_id), instead of .filter() which returns a queryset. There is also a shortcut get_object_or_404 that takes a common pattern of wrapping a .get() into a try..except block for you. If the model is not found, an HTTP 404 page is returned instead.
Use a ModelForm instead
A ModelForm accepts an instance parameter of an existing model so you don't need to set initial values.
With these things in mind your code would end up looking something like this:
# forms.py
from django import forms
class PageForm(forms.ModelForm):
class Meta:
model = Page
fields = ('title', 'image', 'content')
# views.py
from django.shortcuts import render, get_object_or_404
def edit_page(request, page_id):
page = get_object_or_404(Page, pk=page_id)
if request.method == 'POST':
form = PageForm(instance=page, request.POST)
if form.is_valid():
form.save()
else:
form = PageForm(instance=page)
return render(request, 'some_template.html', {'form': form})
def create_page(request):
if request.method == 'POST':
form = PageForm(request.POST)
if form.is_valid():
form.save()
else:
form = PageForm()
return render(request, 'some_template.html', {'form': form})
# urls.py
(r'^page/create/$', views.create_page, 'create_page'),
(r'^page/edit/(?P<page_id>\d+)/$', views.edit_page, 'edit_page'),

Categories

Resources