How to let a User upload an image in Django - python

Hi Guys
I started coding in Django and i just wanted to make an 9gag-Clone.
I followed some Tutorials and acctualy made a Blog. But when i "upload" Images it allways take the default value.
So here is my Html:
{% extends "posts/post_base.html" %}
{% load bootstrap3 %}
{% block post_content %}
<h3 class="title">Poste Some Memes here</h3>
<form action="{% url 'posts:create' %}" method="POST">
{% csrf_token %}
{% bootstrap_form form %}
<input type="submit" value="posten" class="btn btn-primary btn-large">
</form>
{% endblock %}
Here is my Views.py:
class CreatePost(LoginRequiredMixin, SelectRelatedMixin, generic.CreateView):
fields = ('title','picture','group')
model = models.Post
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.save()
return super().form_valid(form)
and at least my models.py:
class Post(models.Model):
user = models.ForeignKey(User,related_name='posts')
created_at = models.DateTimeField(auto_now=True)
title = models.CharField(max_length=30,default='Titel')
picture= models.ImageField(upload_to=settings.MEDIA_ROOT, default='/static/img/default.png')
title_html = models.CharField(max_length=30,default='Titel', editable = False)
group = models.ForeignKey(Group,related_name='posts',null=True,blank=True)
def __str__(self):
return self.title
def save(self,*args,**kwargs):
self.title_html =misaka.html(self.title)
super().save(*args,**kwargs)
def get_absolute_url(self):
return reverse('posts:single',kwargs={'username':self.user.username,'pk':self.pk})
class Meta:
ordering = ['-created_at']
#unique_together = ['user','title','bild']
urls.py and other htmlfiles work correctly.
everything was makemigrated and migrated
I just need to know why it dont save the Images, or dont upload it.

Just replace
<form action="{% url 'posts:create' %}" method="POST">
with
<form action="{% url 'posts:create' %}" method="POST" enctype="multipart/form-data>

Uploaded image are at self.request.FILES
self.object.picture = self.request.FILES or self.request.FILES.get('key')
self.object.save()
You can POST data in self.request.POST and file in self.request.FILES

Both answers where right so couldn't pick them bouth.
So here is the solution:
Added
enctype="multipart/form-data"
to my HTML-Form
AND
added
self.bild = self.request.FILES['bild']
to my CreatePost

Related

Image from an ImageField ModelForm not uploading into the server

Hi so I am new to Django and one of the things I'm trying to do is make a simple gallery application. Somehow I can't add images through the server via the forms if I use a Model Form although I can do it using a plain form. I've tried a lot of the stuff in here and also tried some Youtube stuff but it didn't still work.
Here is my models.py
from django.db import models
from django.urls import reverse
from django.core.validators import validate_image_file_extension
from django.core.files.storage import FileSystemStorage
fs = FileSystemStorage(location='/media')
class FavoriteImages(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(favorite=True)
# Create your models here.
class Photo(models.Model):
name = models.CharField(max_length=120, null=True)
photo = models.ImageField(storage=fs, upload_to='media/', validators=[validate_image_file_extension])
date_uploaded = models.DateTimeField(auto_now=True)
favorite = models.BooleanField(default=False, blank=False)
slug = models.SlugField(null=True, blank=True)
gallery = models.Manager()
gallery_favorites = FavoriteImages()
class Meta:
ordering = ['-date_uploaded']
My Views.py
from PIL import Image
def image_new(request, *args, **kwargs):
Image.init()
form = PhotoForm(data=request.POST, files=request.FILES)
if request.method == 'POST':
form = PhotoForm(request.POST, request.FILES)
if form.is_valid():
form.save()
redirect('../all')
context = {
'form': form
}
return render(request, "form.html", context)
My forms.py
class PhotoForm(forms.ModelForm):
name = forms.CharField(label='',widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':'Title'}))
photo = forms.ImageField(widget=forms.FileInput(attrs={'class':'form-control'}))
favorite = forms.BooleanField(label='Mark as Favorite',widget=forms.CheckboxInput(attrs={'class':'form-check-input'}))
class Meta:
model = Photo
fields = ['name',
'photo',
'favorite']
my .html
{% extends "base.html" %}
{% block content %}
{% if form.is_multipart %}
<form enctype="multipart/form-data" method="post">
This form is a multipart.
{% else %}
<form method="post">
{% endif %}
{% csrf_token %}
{% if form.media %}
{{ form.media }}
{% endif %}
{{ form.as_p }}
<input type="submit" class="btn btn-primary" value="Save"/>
</form>
{% endblock %}
I've placed this in the settings:
MEDIA_URL = 'media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
Something I noticed:
The media folder (root) remains empty, but Model.photo has an url. (not null)
How do I modify my form so that the image gets posted?
EDIT: I fixed it by changing the widget of the ImageField. I don't know why it works now, but it does. Thanks for all the help
Why all those {% if %} in the template? I think it's unecessary. I would write it as follows,
{% extends "base.html" %}
{% block content %}
<form enctype="multipart/form-data" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary" value="Save"></button>
</form>
{% endblock %}
Your form can also be simplified as such,
class PhotoForm(forms.ModelForm):
name = forms.CharField(label='',widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':'Title'}))
photo = forms.ImageField()
favorite = forms.BooleanField(label='Mark as Favorite',widget=forms.CheckboxInput(attrs={'class':'form-check-input'}))
'''
You don't need the Meta class when inheriting from forms.ModelForm. I think the widgets aren't necessary, unless you need to style with CSS specifics.
Your view can also be simplified quite a bit. You don't need PIL Image unless you are modifying your image.
I would write like this,
def image_new(request, *args, **kwargs):
form = PhotoForm()
if request.method == 'POST':
form = PhotoForm(request.POST, request.FILES)
if form.is_valid():
form.save()
redirect('../all')
context = {
'form': form
}
return render(request, "form.html", context)
Django will take care of saving the image to your media folder and assigning it to the ImageField in the model.

Django CreateView doesn't save object and doesn't give error

I'm working on a new project and I'm having difficulties adding new objects from the users page. It can be added from admin dashboard.
This is the model:
class Product(models.Model):
title = models.CharField(max_length=150)
price = models.IntegerField()
image = models.ImageField(upload_to='products')
description = models.TextField(max_length=500)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('product-details', kwargs={'pk': self.pk})
I have this view:
class ProductCreateView(LoginRequiredMixin, CreateView):
model = Product
fields = ['title', 'image', 'description', 'price']
def form_valid(self, form):
form.instance.owner = self.request.user
#form.save() # tried this too and it didn't work
return super().form_valid(form)
product_form.html:
{% extends "index/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Product</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Save</button>
</div>
</form>
</div>
{% endblock content%}
I tried a couple of times and I didn't work. So I searched for solutions and tried the following:
instance = form.save(commit=False)
instance.owner = self.request.user
instance.save()
return super().form_valid(instance)
and this
self.object.owner = self.request.user
self.object = form.save()
return super(ProductCreateView, self).form_valid(form)
within the form_valid(). Neither of them worked. So I can open the form and fill the fields. When I send it, the object is not saved but it doesn't give any error. It just reloads the form.
Ok, so after nearly one week of trying to fix the issue and not much help, I found a solution! form_valid() works fine the way it is, but the problem is in the form. Adding enctype fixes the problem:
<form method="POST" enctype="multipart/form-data">
The explanation I found for this is that without enctype the image data is not being passed correctly to the database. So it looks like the form is fine, but on the background it is not saving the image.

Setting initial field value in generic form through URL parameter

I'm struggling to set an initial value in a form instance based on the URL parameter in Django 3.0.
I have a Claim model:
# models.py
class Claim(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
text = models.TextField()
date_added = models.DateTimeField(default=timezone.now)
member = models.ForeignKey(User, on_delete=models.CASCADE)
I have a NewClaimForm based on ModelForm:
# forms.py
class NewClaimForm(forms.ModelForm):
class Meta:
model = Claim
fields = ['product', 'text']
I have a NewClaimView based on CreateView:
# views.py
class NewClaimView(LoginRequiredMixin, CreateView):
model = Claim
form_class = NewClaimForm
template_name = 'portal/new_claim.html'
def form_valid(self, form):
form.instance.member = self.request.user
return super(NewClaimView, self).form_valid(form)
And using the following template fragment on the index page...
# index.html
<div class="card-deck">
{% for product in products %}
<div class="card text-center">
<div class="card-header">
<h5 class="card-title text-primary">{{ product }}</h5>
</div>
<div class="card-body">
<ol class="card-text text-left">
<li>Fill in the {{ product }} form</li>
<li>Attach your medical records</li>
<li>Get your claim reviewed within 48 hours</li>
</ol>
Online Form
</div>
</div>
{% endfor %}
</div>
...I pass the product_id parameter to the URL:
# urls.py
app_name = 'portal'
urlpatterns = [
path('new_claim/<int:product_id>/', NewClaimView.as_view(), name='new_claim_product'),
]
And lastly, this is what my new_claim template looks like:
# new_claim.html
{% extends "portal/base.html" %}
{% load bootstrap4 %}
{% block content %}
<p>Submit a new claim</p>
<form action="{% url 'portal:new_claim' %}" method='post' class="form" enctype="multipart/form-data">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button name="submit">Submit claim</button>
{% endbuttons %}
</form>
{% endblock content %}
I would like to now use the product_id to set the initial value of the product field in the form instance according to product_id. How can I achieve this?
Figured out how to do this by modifying the get method for my class-based view:
# views.py
class NewClaimView(LoginRequiredMixin, CreateView):
model = Claim
form_class = NewClaimForm
template_name = 'portal/new_claim.html'
def get(self, request, product_id=None, *args, **kwargs):
form = self.form_class(initial={'product': product_id})
return render(request, self.template_name, {'form': form})
def form_valid(self, form):
form.instance.member = self.request.user
return super(NewClaimView, self).form_valid(form)
Here's a link to the relevant documentation.
Is this an optimal way to solve this?
Do I need the *args and **kwargs in the modified get method? I'm not using them in my code but perhaps it would be useful to keep them there for other purposes in the future?

Comment isn't getting deleted

I made a comment model for a blog and I wanted to give the user a way to delete the comment so I made a function based view for it but it didn't work so I decided to use a class based view but both of the views give the same error. the only thing that happens is that the url gets a ? after it and the page just refreshes as it is. The function based and class based views are both given below
func based
def comment_delete(request, pk):
comment_to_delete=get_object_or_404(comment,pk=pk)
if request.method=='POST':
post_url=comment_to_delete.content_object.get_absolute_url()
comment_to_delete.delete()
messages.success(request, 'Your comment has been deleted')
return HttpResponseRedirect(post_url)
context={
'comment':comment_to_delete
}
return render(request, 'blog/confirm_delete.html', context)
class based
class DeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = comment
success_url = '/'
def test_func(self):
comment= self.get_object()
if self.request.user == comment.user:
return True
return False
html of confirm page
{% extends 'blog/base.html' %}
{% block content %}
<form>
<p>are you sure you want to delete {{ comment }}</p>
<input type="submit" value="confirm" >
</form>
{% endblock %}
models.py
class comment(models.Model):
post=models.ForeignKey(Blog, on_delete=models.CASCADE)
user=models.ForeignKey(User, on_delete=models.CASCADE)
content=models.TextField(max_length=160)
timestamp=models.DateTimeField(auto_now_add=True)
def __str__(self):
return '{}-{}'.format(self.post.title,str(self.user.username))
def get_absolute_url(self):
return reverse('comment', kwargs={"pk": self.pk})
You need to add a post request to your form. Post requests need tokens to protect from Cross Site Request Forgeries. Normally a token is passed with every post request.
<form method="POST">
{% csrf_token %}
<p>are you sure you want to delete {{ comment }}</p>
<input type="submit" value="confirm" >
</form>
The problem is in your template, not your views. You need to add method="post" to the form to do a POST request, and add {% csrf_token %} to prevent a CSRF error.
<form method="post">
{% csrf_token %}
<p>are you sure you want to delete {{ comment }}</p>
<input type="submit" value="confirm" >
</form>

Django - Comment form in an existing template. How to define it in views.py?

I have 4 models: Post, Comment, Blogger and User.
I have an post_description template, in below of that, I have placed a comment form.
But how to define it in views? My problem is - to get its username, like the user who is logged in will be stored as "posted_by" and in which blog post he post will be stored as "topic" of the blog.
How to store these information, so they get automatically added?
Form that i has described in post_desc.html
{% if user.is_authenticated %}
<form method="post">
{% csrf_token %}
<input type="text" name="comment" style="width: 800px; height: 145px;">
<button type="submit">Submit Comment</button>
</form>
{% else %}
<p>Login to comment</p>
{% endif %}
Current view of that post_desc:
def post_desc(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, 'post_desc.html', {'post': post})
Now the user can be accessed as follows in the views:
user = request.user
And about the Topic, maybe you could add a hidden input in your form to get blog id , as you are already passing the post in the form template. :
<form method="post">
{% csrf_token %}
<input type="text" name="comment" style="width: 800px; height: 145px;">
<input type="hidden" name="topic" value="{{ post.id }}">
<button type="submit">Submit Comment</button>
And when posted in the view you can get blog by:
post_id = request.POST.get('topic')
post = get_object_or_404(Post, pk=post_id)
And then finally proceeding with your actual flow.
I think what you need here is basic model form setup.
I am hoping there is a blog entry and comments associated with it and you want a commenting functionality on each post.
This is rough quick answer.
Your models.py looks like this:
from django.db import models
from django.conf import settings
class Comments(models.Model):
posted_by = models.ForeignKey(settings.AUTH_USER_MODEL)
topic = models.ForeignKey(Blog)
comment = models.TextField()
last_modified = models.DateTimeField(auto_now=True)
created_on = models.DateTimeField(auto_now_add=True)
You setup a model form in your forms.py
from django.forms import ModelForm
from .models import Comments
class CommentForm(ModelForm):
class Meta:
model = Comments
fields = ['comment']
You setup a model form post view.
#login_required
#require_http_methods(["POST"])
def post_comments_controller(request, identifier):
from .forms import CommentForm
comment_form = CommentForm(request.POST or None)
if comment_form.is_valid():
comment_obj = comment_form.save(commit=False)
topic = Blog.objects.get(id=identifier)
comment_obj.posted_by = request.user
comment_obj.item = topic
comment_obj.save()
return HttpResponse("Done")
else:
return HttpResponseBadRequest()
You setup a entry point in your urls.py
from django.conf.urls import patterns, url
from django.conf import settings
urlpatterns = patterns('',
url(r'^/blog/(?P<identifier>[d]+)/comment$',
'views.post_comments_controller', name='post_comment')
)
And your finally the html form
{% if user.is_authenticated %}
<form method="POST" action="{% url 'post_comment' blog.id %}">
{% csrf_token %}
<input type="text" name="comment" style="width: 800px; height: 145px;">
<button type="submit">Submit Comment</button>
</form>
{% else %}
<p>Login to comment</p>
{% endif %}
This is not tested overall. Let me know.
From Django docs you can use FormMixin with DetailView like this:
class AuthorInterestForm(forms.Form):
message = forms.CharField()
class AuthorDetail(FormMixin, DetailView):
model = Author
form_class = AuthorInterestForm
def get_success_url(self):
return reverse('author-detail', kwargs={'pk': self.object.pk})
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden()
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
# Here, we would record the user's interest using the message
# passed in form.cleaned_data['message']
return super().form_valid(form)

Categories

Resources