Upload Image in the wrong directory in django ImageField - python

When I upload a photo, the photo is loaded successfully, but the photo is placed in the wrong directory.
Instead of placing the image on the path to 'media/posts-pics/' - as I have outlined in my Post model - it is placed on the 'media' path.
These are my files:
models.py
class Post(models.Model):
index_pic = models.ImageField(upload_to='posts-pics/')
Project.urls.py
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
views.py
def add_post(request):
if request.method == "POST":
form = AddPostForm(request.POST, request.FILES)
if form.is_valid():
new_post = form.save(commit=False)
new_post.index_pic = form.cleaned_data['index_pic']
new_post.save()
return redirect('view_post')
else:
form = AddPostForm()
template = 'post/add_edit_post.html'
context = {'form': form}
return render(request, template, context)
def edit_post(request, slug):
post = get_object_or_404(Post, slug=slug)
if request.method == "POST":
form = AddPostForm(request.POST, request.FILES, instance=post)
if form.is_valid():
Post.objects.filter(id=post.id).update(title=request.POST['title'],
index_pic=form.cleaned_data['index_pic'],
)
return redirect('view_post')
else:
form = AddPostForm(instance=post)
template = 'post/add_edit_post.html'
context = {'form': form}
return render(request, template, context)
I used exactly the same code for add_post, and the photo was in its place, but I got into trouble in edit_post. what's wrong ?
Notice:
Technically I can delete 'media/post-pics' but this is done with a
special purpose and the purpose is: Each app have its folder for
saving images.

The problem is that you're no using your ModelForm the right way.
In the edit_post view, you want to replace this:
Post.objects.filter(id=post.id).update(
title=request.POST['title'],
index_pic=form.cleaned_data['index_pic'],
)
with a plain simple:
form.save()
which will take care of the updating the post passed as form.instance (using sanitized data, which is not the case with your current code)
FWIW, in your add_post view, you also want to replace this
new_post = form.save(commit=False)
new_post.index_pic = form.cleaned_data['index_pic']
new_post.save()
with a plain simple:
new_post = form.save()
Once again, the whole point of ModelForms is that they know how to create and update model instances.

Related

In django, how can I ensure that a specific user is accessing a view?

I would like to be able to check if the user accessing a page is a specific user.
For instance, when a user accesses the "Edit Post" page on my blog app, I want to ensure that the user is the author of the post.
Currently, I check that the user accessing '/Blog/Edit//' has the blog.change_post permission.
However, if a second user (who also has that permission) were to enter the URL to change someone else's post, they would pass that permission check and be able to edit someone else's post.
What I want is a #user_passes_test function that check's the user object accessing the view against the author attribute of the post.
#/Blog/urls.py
urlpatterns = [
...
path('Edit/<int:pk>', views.BlogEdit, name='BlogEdit'),
...
]
#/Blog/views.py
#permission_required('blog.change_post', login_url='/Portal/login')
def BlogEdit(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = PostForm(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.save()
return redirect('/Blog', pk=post.pk)
else:
form = PostForm(instance=post)
return render(request, 'Blog/Edit.html', {'form': form})
You can add an extra filter to your get_object_or_404:
#permission_required('blog.change_post', login_url='/Portal/login')
def BlogEdit(request, pk):
post = get_object_or_404(Post, pk=pk, author=request.user)
if request.method == "POST":
form = PostForm(request.POST, instance=post)
if form.is_valid():
form.save()
return redirect('/Blog', pk=post.pk)
else:
form = PostForm(instance=post)
return render(request, 'Blog/Edit.html', {'form': form})
Here author is the hypothetical ForeignKey from Post to the user model. It is possible that the name is different, but the idea is still the same.
This thus means that in case the pk is the pk of a Blog for which request.user is not the author, then we will have a 404 response.
The advantage of filtering here, is that we use a single query for the filtering. We will not (lazily) load the author to check if it is the same as the logged in user.
Note: post = form.save(commit=False) and post.save() are equivalent to post = form.save() (so with commit=True).
In a class based view change the get_queryset method to filter by current user.
class MyView(LoginRequiredMixin, UpdateView):
...
def get_queryset(self):
"""
Only the current logged in user can edit ...
"""
return super().get_queryset().filter(created_by=self.request.user)
I figured it out from This Post in the end. What urbanspaceman suggests works for class based views, but for function based views you can do:
#permission_required('blog.change_post', login_url='/Portal/login')
def BlogEdit(request, pk):
post = get_object_or_404(Post, pk=pk)
if post.author != request.user:
raise Http404("You are not allowed to edit this Post")
# ... Rest of view here ... #

Django is not saving form to datebase

I got the problem with form that is not saving to the datebase.
views.py
...
#login_required
def create_task(request):
if request.method == 'POST':
form = CreateTaskForm(request.POST, instance=request.user)
if form.is_valid():
form.save()
return redirect('index')
else:
form = CreateTaskForm()
context = {'form': form}
return render(request, 'tasks/task_form.html', context)
...
forms.py
from django import forms
from .models import Task
class CreateTaskForm(forms.ModelForm):
class Meta:
model = Task
fields = (
'name',
'end_date',
'description',
)
Is it the problem with a create_task view or CreateTaskForm?
The problem is with the create_task view:
if request.method == 'POST':
form = CreateTaskForm(request.POST)
if form.is_valid():
# if you have user in your model task make commit to false then set the user
form.save(commit=false)
form.user = request.user
#if not save directly you form
form.save()
return redirect('index')
then check if your url is:
path('create_task', create_task, name="create_task")
First off, since you are using POST, which means you are creating, there is no need for instance=request.user - which is used to compare previous data against new data provided by the form. Looking at your form, it does not look like it has anything to do with the user.
Second, you are not displaying or returning form errors. So it may be silently failing validation at the model level without your knowledge. Try adding a return with form errors.
#login_required
def create_task(request):
form = CreateTaskForm()
if request.method == 'POST':
form = CreateTaskForm(request.POST)
if form.is_valid():
form.save()
return redirect('index')
context = {'form': form}
return render(request, 'tasks/task_form.html', context)
You can look in form.errors on the template side to see validation errors.

getting "the field name is required" after uploading the image

after uploading the image to a form when i submit the form it still shows "the field is required" error
in models.py:
class Members(models.Model):
image=models.ImageField(upload_to="images/",default='')
in forms.py:
class CreateOne(forms.Form):
image=forms.ImageField(required=True)
in create.html:
<form class="form-inline" action="create" method="POST"
enctype="multipart/form-data" novalidate>
in views.py:
def create(request):
member = Members(image=request.FILES['image'])
if request.method == 'POST':
form = forms.CreateOne(request.POST)
if form.is_valid():
member.save()
return redirect('/')
else:
form = CreateOne()
I think you understand the way to use a Form the wrong way. The idea of such Form when you write some_form.save() it makes the changes (and for example create a model instance and save it to the database).
Since most forms (like probably this one) are related to a single model, Django has a ModelForm, which offers extra convenience:
class CreateOne(forms.ModelForm):
image=forms.ImageField(required=True)
class Meta:
model = Members
fields = ['image']
Then we can make a view that will create an instance in case of a valid form with:
def create(request):
if request.method == 'POST':
form = forms.CreateOne(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('/')
else:
form = CreateOne()
return render(request, 'some_template.html', {'form': form}
You probably also do not want to redirect in case the form was not valid, since then you probably want to give the user feedback that the form was not valid (and show the corresponding errors).

Django user specific folders

I want to create user specific folders for files uploaded by users. This is my views.py:
#login_required
def list(request):
# Handle file upload
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Document(docfile = request.FILES['docfile'])
newdoc.save()
# Redirect to the document list after POST
return HttpResponseRedirect(reverse('upload.views.list'))
else:
form = DocumentForm() # An empty, unbound form
# Load documents for the list page
documents = Document.objects.all()
# Render list page with the documents and the form
return render_to_response(
'upload/list.html',
{'documents': documents, 'form': form},
context_instance=RequestContext(request)
)
This is my models.py:
class Document(models.Model):
docfile = models.FileField(upload_to='uploads/%Y.%m.%d')
My idea is this. Instead of naming it uploads/%Y.%m.%d I stick the username somewhere in there. Is there a way to do that?
I do something like that in my models.py:
def _upload_path(instance,filename):
return instance.get_upload_path(filename)
class Document(models.Model):
docfile = models.FileField(upload_to=_upload_path)
user = models.ForeignKey('auth.User')
def get_upload_path(self,filename):
return "static/uploads/"+str(self.user.id)+"/"+filename

Django ImageField: files dont get uploaded

I implemented some ImageFields in my model and installed PIL (not the cleanest install). Things seem to work as I get an upload button in the admin and when I call the .url property in the view I get the string with the filename + its upload property.
The problem is that the file is not there, apparently it doesnt get uploaded once I save the model.
Any idea?
Thanks
Here's a sample of my code situation
models.py
class My_Model(models.Model):
[...]
image = models.ImageField(upload_to = 'images/my_models/main')
view.py
'image': query.my_model.image.url
result:
static/images/my_models/main/theimage.png
Make sure that you're binding request.FILES to the form when POSTing, and that the form is declared as multi-part in the template
Here's the view from one of my applications:
#login_required
def submit(request):
if request.method == 'POST':
(Photo.objects.count()+1, request.FILES['photo'].name.split(".")[1]), request.FILES['photo'])}
form = PhotoForm(request.POST, request.FILES)
if form.is_valid():
new = Photo(photo=request.FILES['photo'], name=request.POST['name'])
new.save()
return HttpResponseRedirect('/') # Redirect after POST
else:
form = PhotoForm()
return render_to_response('app/submit.html', {'form': form}, context_instance=RequestContext(request))
and the PhotoForm class:
class PhotoForm(forms.ModelForm):
class Meta:
model = Photo
fields = ('name', 'photo')

Categories