Django form to update an object doesn't keep files - python

So I'm working on a portfolio site, and I've got a form called Work that represents the works in the portfolio. A Work has an M2M field for normal images, and one ImageField for the main image that is to be used for thumbnails etc.
My problem is the following, when I go to the update view I created for Work, the old Image (that is already in the database) is listed in the form, but no in the field itself. It says current: [name of the image], and then the regular filefield with label edit.
I don't want the user to only be able to update a work if they upload the image again. How do I pass the current image to the form?
#models.py
class Work(models.Model):
name = models.CharField(max_length=255)
image = models.ImageField( upload_to="images" )
#forms.py
class Meta:
model = Work
exclude = ('slug',)
#views.py
def workEdit(request, pk):
if request.method == "POST":
form = WorkForm(request.POST, request.FILES)
if form.is_valid():
new_work = form.save(commit=True)
return redirect("/portfolio/beheer/werk")
else:
print(form)
print(form.errors)
else:
work = get_object_or_404(Work, pk=pk)
form = WorkForm(request.POST ,request.FILES, instance=work)
context = {
'form': form,
}
return render(request, 'submit.html', context)

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-How can I upload image to a Model, without that field being present in the ModelForm

I am trying to build an image steganography application and for the encoding part I have a form with two fields, one for image and one for the message that is to be encoded. The encoding part takes place in views.py, but I do not seem to manage how to upload the encoded image in the ImageModel in order to use it later for the decoding part.
1. If I include image_e in the form as a hidden field, I get an error that it needs to be submitted
2. If somehow the form is being submitted, the model contains only the two fields present in the form, 'image' and 'message', and for 'image_e' i get None or the default if i have that option
3. Right now, if I fill the form I get the defaults for the image fields, but not for the char one
I actually only need 'image_e' stored, but I thought that if I inherit all of the fields from the model it would be easier. I am a beginner in Django and this is my first more complex application.
models.py
class Image(models.Model):
image = models.ImageField(upload_to='images', default='default.png')
message = models.CharField(max_length=200, default=1)
image_e= models.ImageField(upload_to='encoded', blank=True)
forms.py
class EncodeForm(forms.ModelForm):
#method= forms.CharField(label='How would you like to encode this image?', widget=forms.Select(choices=METHOD_CHOICES))
"""Form for the image model"""
class Meta:
model = Image
fields=('image', 'message', 'image_e')
def encode(self):
pass
views.py
def image_upload_view(request):
"""Process images uploaded by users"""
if request.method == 'POST':
form = EncodeForm(request.POST, request.FILES)
#form.fields['image'].save(im.name, im, save=True)
#form.fields['image_e'].save(image_e.name, image_e, save=True)
if form.is_valid():
msg=request.POST.get('message')
im=request.FILES.get('image')
image_e=encryptImage(im,msg)
data={
'image':im,
'message':msg,
'image_e':image_e
}
form=EncodeForm(data)
#Image.objects.create(**data)
img_obj = form.instance
form.save()
return render(request, 'img_steg/encode.html', {'form': form,'img_obj':img_obj})
else:
form = EncodeForm()
return render(request, 'img_steg/encode.html', {'form': form})
This is how ImageModel looks from the admin view if i submit the form
Edit: This is what the data dictionary contains:
{'image': <InMemoryUploadedFile: landscape_0.jpg (image/jpeg)>,
'message': 'ttttt',
'image_e': <InMemoryUploadedFile: im_enc_cfc97e92-b2ef-421d-a108-89f3948ce1ab (image/jpeg)>
}

Django: update a model with the current user when an non-model form field is changed

I'm building a page that allows users to edit Task and related Activity records (one task can have many activities), all on the same page. I want to allow the user to "adopt" one or more activities by ticking a box, and have their user record linked to each activity via a ForeignKey. Here are extracts from my code...
models.py
from django.contrib.auth.models import User
class Task(models.Model):
category = models.CharField(max_length=300)
description = models.CharField(max_length=300)
class Activity(models.Model):
task = models.ForeignKey(Task, on_delete=models.CASCADE)
title = models.CharField(max_length=150)
notes = models.TextField(blank=True)
owner = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
The activity "owner" is linked to a User from the Django standard user model.
I added an extra field in the form definition for the adopt field - I don't want to add it to the model as I don't need to save it once it's done it's job.
forms.py
class ActivityForm(forms.ModelForm):
adopt = forms.BooleanField(required=False)
class Meta:
model = Activity
fields = '__all__'
views.py
def manage_task(request, pk):
task = Task.objects.get(pk = pk)
TaskInlineFormSet = inlineformset_factory(Task, Activity,
form = ActivityForm)
if request.method == "POST":
form = TaskForm(request.POST, instance = task)
formset = TaskInlineFormSet(request.POST, instance = task)
if form.has_changed() and form.is_valid():
form.save()
if formset.has_changed() and formset.is_valid():
## ? DO SOMETHING HERE ? ##
formset.save()
return redirect('manage_task',pk=task.id)
else:
form = TaskForm(instance = task)
formset = TaskInlineFormSet(instance = task)
context = {'task': task, 'task_form': form, 'formset': formset}
return render(request, 'tasks/manage_task.html', context)
When the adopt field is ticked on the form, I want to be able to set the owner field in that form to the current user before the associated model instance is updated and saved.
I just can't figure out how to do that - if it was a single form (rather than an InlineFormSet), I think I could put code in the view to change the owner value in the form field before it was saved (I haven't tried this). Or try save(commit = False) and update the model instance then save() it.
Maybe I have to iterate through the formset in the view code and try one of those options when I find one that had adopt=True?
When the adopt field is ticked on the form, I want to be able to set the owner field in that form to the current user before the associated model instance is updated and saved.
formset = TaskInlineFormSet(request.POST, instance = task)
if formset.adopt:
# If True
formset.user = request.user
formset.save()
I think I could put code in the view to change the owner value in the form field before it was saved (I haven't tried this).
You should give it a try.
I'm not happy with this solution but it does work. I iterate through the forms and change the object instance if my adopt field is set.
views.py
def manage_task(request, pk):
task = Task.objects.get(pk = pk)
TaskInlineFormSet = inlineformset_factory(Task, Activity,
form = ActivityForm)
if request.method == "POST":
form = TaskForm(request.POST, instance = task)
formset = TaskInlineFormSet(request.POST, instance = task)
if form.has_changed() and form.is_valid():
form.save()
if formset.has_changed() and formset.is_valid():
## HERE'S WHAT I ADDED ##
for form in formset:
if form.cleaned_data['adopt'] is True:
form.instance.owner = request.user
## END OF ADDITIONS ##
formset.save()
## return redirect('manage_task',pk=task.id) # CHANGED THIS BECAUSE I WASN'T RETURNG ERRORS!
if not form.errors and not formset.total_error_count():
return redirect('manage_task',pk=task.id)
else:
form = TaskForm(instance = task)
formset = TaskInlineFormSet(instance = task)
context = {'task': task, 'task_form': form, 'formset': formset}
return render(request, 'tasks/manage_task.html', context)
I wish I could find more in the docs about how the form saving works but I think I'll have to look into the code if I want more detail.

Adding additional data fields when uploading file to Database w/ Django

I'm new here and also new to Django and any kind of web development.
Right now, I am working on a project that allows users to submit academic papers to be edited by other users.
The problem I am having is that I would like to allow users to upload a file and with that file, upload some data about the file (meta data?) such as the title of the paper (listed as titleField) prompt, etc.
I have been able to find answers on how to only upload the file, but not on how to upload the data and the file as a whole package.
If anyone can shed any light on this, that would help me a lot!
Here is the models.py:
from django.db import models
class Document(models.Model):
docfile = models.FileField(upload_to='documents/%Y/%m/%d')
titleField = models.CharField(max_length=100, default="")
dueDateField = models.IntegerField(default=10)
classNumField = models.IntegerField(default=0)
promptField = models.CharField(max_length=300, default="")
And below is the function that uploads the file. I understand how this section works, however it is uploading the additonal data from the forms that confuses me:
views.py
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('ReadMyPaper.myapp.views.list'))
else:
form = DocumentForm() # A 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(
'myapp/list.html',
{'documents': documents, 'form': form},
context_instance=RequestContext(request)
)
Are you using a ModelForm ? If no, thats how to create one:
# forms.py
from django import forms
from .models import Document
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
And in both cases, how to properly use it:
# views.py
from .forms import DocumentForm
def document_list(request):
# Handle file upload
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
new_doc = form.save()
# Redirect to the document list after POST
return HttpResponseRedirect(reverse('ReadMyPaper.myapp.views.list'))
else:
form = DocumentForm() # A 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(
'myapp/list.html',
{'documents': documents, 'form': form},
context_instance=RequestContext(request)
)
First point : Django forms (the non-ModelForm ones) do two things:
they display their fields, and
they sanitize and validate their data.
So when using Djago forms, you shouldn't have to access request.POST (or request.GET in the rare cases you have a GET form) directly, only form.cleaned_data.
Second point: ModelForms also know
how to create their own fields based on the associated model (but you can restrict / override these fields manually) and
create or update a model instance based on their (validated) data.
So when using a ModelForm, you shouldn't even have to do anything with the model itself in your view (for the most common use cases at least).

How to save path to image when I create object using forms

I have a such model in django 1.6:
class Instance(models.Model):
...
image = models.ImageField(upload_to='flats_photo',null=True, blank=True)
...
Form:
class InstanceForm(ModelForm):
class Meta:
model = Instance
fields=[...,'image',...]
When I create new object I use such view:
def add_instance(request):
if request.POST:
form=InstanceForm(request.POST)
if form.is_valid():
f = InstanceForm(request.POST)
new_instance=f.save()
else:form=InstanceForm()
locals().update(csrf(request))
return render_to_response(...)
All fields of new object create, but not field image.There is no image. In django admin I see: Image no file selected. Everything work good when i add object from admin. How to solve this problem
the file data isn't in request.POST it's in request.FILES
https://docs.djangoproject.com/en/dev/topics/http/file-uploads/#handling-uploaded-files-with-a-model
Change your function to something like
def add_instance(request):
if request.POST:
form=InstanceForm(request.POST, request.FILES)
if form.is_valid():
new_instance=form.save()
else:
form=InstanceForm()
locals().update(csrf(request))
return render_to_response(...)

Categories

Resources