Im trying to submit a form with some textfields and an imagefield. And save it as a new model object. Cant get it to work so im going over each part from the start now..
How do i handle (just saving it) the imagefield in my views? Do i need to write an extra handler? It's not a modelform but a regular form. Where does PIL/PIllow come into this. Should I import it? Can i clean the imagefield?
I've been trying this:
def UploadThis(request):
if request.method == 'POST':
form = TestForm(request.POST, request.FILES)
response = {}
if form.is_valid():
response["status"] = "OK"
justtesting = Newmodel()
cd = form.cleaned_data
justtesting.name = cd['name']
justtesting.type = cd['type']
justtesting.description = cd['description']
justtesting.picture = request.FILES['picture']
justtesting.save()
Model:
class Newmodel(models.Model):
name = models.CharField(max_length=50)
type = models.CharField(max_length=50)
description = models.CharField(max_length=140, blank=True)
picture = models.ImageField(upload_to='images/testfiles', blank=True)
def __unicode__(self):
return self.name
Form:
class TestForm(forms.Form):
name = forms.CharField(max_length=50)
type = forms.CharField(max_length=50)
description = forms.CharField(max_length=140)
picture = forms.ImageField()
def clean(self):
cleaned_data = self.cleaned_data
name = cleaned_data.get("name")
description = cleaned_data.get("description")
type = cleaned_data.get("type")
return cleaned_data
Your error is that you are trying to assign the image to the model field using request.POST, when you actually need to use form.cleaned_data like so:
justtesting.picture = form.cleaned_data['picture']
Your clean method on the form is not doing anything currently. You could do validation (although that is not neccessary) by using a clean method something like this:
def clean_image(self):
image = self.cleaned_data.get('image', None)
if image:
# do some validation, if it fails
raise forms.ValidationError(u'Form error')
return image
For an example that validates the image size see this question: https://stackoverflow.com/a/16425455/1744645
Using PIL/Pillow is not necessary and only required if you want to some form of image processing. You could have a save method on your model, for example to resize the image:
def save(self,force_insert=False, force_update=False, *args, **kwargs):
# save everything else
super(Newmodel, self).save(force_insert, force_update)
if self.image:
if self.image.width > 300 or self.image.height > 300:
resize_image(self.image)
PIL/Pillow itself has the following basic operations that might be of interest:
img = PIL.open(PATH_TO_IMAGE)
img = img.resize((x, y), PIL.ANTIALIAS)
img = img.crop((0, 0, x, y))
img.save(path,quality=90)
But see http://effbot.org/imagingbook/pil-index.htm for the full docs.
Related
What ImageField does under the hood , is that is stores the image path as string in the db.
I would like to overwrite the field in such a way, that it can accept either a binary image file or absoulte URI path and return the path as string and store it as char in the db.
Which method would have to be overwritten to achieve this operation ?
thanks
Without overriding ImageField() you can do those things using one extra URLField() like this...
models.py
class Product(models.Model):
name = models.CharField(max_length=200, null=True)
image = models.ImageField(upload_to ='Products')
img_abs_url = models.URLField(blank=True,null=True)
def __str__(self):
return self.name
form.py
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = "__all__"
exclude = ['img_abs_url']
views.py
def HomeView(request):
form = ProductForm()
products = Product.objects.all()
if request.method == 'POST':
form = ProductForm(request.POST,request.FILES)
if form.is_valid():
product_name = form.cleaned_data['name']
image = form.cleaned_data['image']
create_product = Product.objects.create(name=product_name,image=image)
create_product.img_abs_url = request.build_absolute_uri(create_product.image.url)
create_product.save()
context = {'form': form, 'products': products}
return render(request, 'index.html', context)
Output (admin panel)
I want to create Note object which one of the fields of the model is an ImageField using Django Rest Framework.
I can already create objects and update all different fields using my API, except for images.
My code:
models.py
class Note(OwnedModel):
note_id = models.UUIDField(primary_key=True,
default=uuid.uuid4,
editable=False)
# note_owner = models.ForeignKey(, null=True, blank=True, on_delete=models.SET_NULL)
note_name = models.CharField(max_length=50)
body = models.TextField()
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
qr_image = models.ImageField(upload_to='notes', null=True)
def __str__(self):
return self.note_name[0:50]
class Meta:
ordering = ['-updated']
views.py
#api_view(['GET', 'POST'])
def getNote(request, pk=None):
if request.method == 'GET':
note = Note.objects.get(note_id=pk)
serializer = NoteSerializer(note, many=False)
return Response(serializer.data)
elif request.method == 'POST':
_data = request.data.copy()
owner = request.user.id
_data["owner"] = owner
# Generate QR code
qr_image = generate_qr(_data["note_name"])
# HOW TO PASS THE IMAGE TO THE SERIALIZER?
_data["qr_image"] = qr_image
# _data["qr_image"] = qr_image[0]
# _data["qr_image"] = qr_image[1]
serializer = NoteSerializer(data=_data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(status=status.HTTP_400_BAD_REQUEST)
serializers.py
class NoteSerializer(ModelSerializer):
class Meta:
model = Note
fields = '__all__'
qr_code.py
import qrcode
def generate_qr(qr_file_name=None):
qr = qrcode.QRCode(
version=1,
# error_correction=qrcode.constants.ERROR_CORRECT_L,
box_size=4,
border=3,
)
qr.add_data(qr_file_name)
qr.make(fit=True)
img = qr.make_image()
# img = qr.make_image(fill_color="black", back_color="white")
path='images/notes/'+str(qr_file_name)+'.jpg'
img.save(path)
return path, img
The QR code is properly generated and saved in the upload path perfectly.
What I cannot manage to build line _data["qr_image"] = qr_image correctly, or if I need to return the image in a different way from the generate_qr function. Everything else it is working well (for example create the Note object form the Admin and uploading the qr image).
#api_view(['GET', 'POST'])
def getNote(request, pk=None):
if request.method == 'GET':
# automatically raise 404 if obj does not exist
note = get_object_or_404(Note.objects, note_id=pk)
# many=False is default, dont need that
serializer = NoteSerializer(note)
return Response(serializer.data)
elif request.method == 'POST':
serializer = NoteSerializer(data=request.data)
# raise_exception=True will raise validation error (400) automatically
serializer.is_valid(raise_exception=True)
# get note name after validation to be sure it has proper length etc.
qr_image = generate_qr(serializer.validated_data.get("note_name"))
# attributes provided here will bypass validation
# and will be injected directly to model create method
serializer.save(
owner=request.user,
qr_image=qr_image
)
return Response(serializer.data, status=status.HTTP_201_CREATED)
Im not sure that your generate_qr function should save image in the given path because Note should do it for you on create.
Tip: check the viewsets.GenericViewSet class - it will properly split your view into methods and makes the life easier.
I'm using django's CreateView to add images to a book. I pass the book's id to the class based view as a parameter in the url. Form fields such as book and language are not rendered on the template, rather they're obtained with the help of the book's id.
# views.py
class PictureCreateView(CreateView):
model = Upload
fields = "__all__"
book_id = None
def get_initial(self):
initial = super(PictureCreateView, self).get_initial()
initial = initial.copy()
self.book_id = self.kwargs['book_id']
book = Book.objects.get(id=self.book_id)
initial['book'] = book
initial['language'] = language
initial['uploader'] = self.request.user
return initial
# set book_id so it used in the template
def get_context_data(self, **kwargs):
context = super(PictureCreateView, self).get_context_data(**kwargs)
context['book_id'] = self.book_id
return context
def form_valid(self, form, **kwargs):
print('Form is valid')
self.object = form.save()
files = [serialize(self.object)]
data = {'files': files}
response = JSONResponse(data, mimetype=response_mimetype(self.request))
response['Content-Disposition'] = 'inline; filename=files.json'
return super(PictureCreateView, self).form_valid(form)
def form_invalid(self, form):
print('Form invalid!')
print(form.errors)
data = json.dumps(form.errors)
return HttpResponse(content=data, status=400, content_type='application/json')
# models.py
class Upload(models.Model):
image = models.ImageField(upload_to=get_upload_path, help_text='Image to process')
uploader = models.ForeignKey(settings.AUTH_USER_MODEL, models.CASCADE, related_name='uploader')
language = models.ForeignKey(Language, models.CASCADE)
book = models.ForeignKey(Book, models.CASCADE)
The problem is that I get an error saying the form is invalid, and the fields uploader, book and language are required. How do I resolve this?
The initial data is used to display the defaults when the form is initially displayed. It isn't used when those values are missing from the submitted form data. If fields like book and uploader are set from the URL or logged-in user, then you should leave them out of the form completely, instead of setting them in the initial data. You can then set the values on the instance in the form_valid method before the form is saved.
from django.contrib.auth.mixins import LoginRequiredMixin
class PictureCreateView(LoginRequiredMixin, CreateView):
model = Upload
fields = ['other_field1', 'other_field2', ...] # leave out book, language and uploader
def form_valid(self, form):
self.book_id = self.kwargs['book_id']
book = Book.objects.get(id=self.book_id)
form.instance.book = book
form.instance.language = ????
form.instance.uploader = self.request.user
return super(
The LoginRequiredMixin makes sure that only logged-in users can access the view.
You may want to use get_object_or_404 to handle the case where book_id refers to a book that does not exist.
One thought, initial doesn't fill the model for submission. You need to do that in init
def __init__(self):
super(PictureCreateView, self).__init__()
self.fields['book'] = self.initial['book']
self.fields['uploader'] = self.initial['uploader']
self.fields['language'] = self.initial['book']
Or, if you don't want to set the fields, make sure they are optional in your original model:
class Upload(models.Model):
uploader = models.ForeignKey('uploader', on_delete=models.CASCADE, null=True, blank=True)
book = models.ForeignKey('book', on_delete=models.CASCADE, null=True, blank=True)
language = models.ForeignKey('language', on_delete=models.CASCADE, null=True, blank=True)
So, I have a system where users can either choose from an already existing image gallery, or upload a new image to be processed and saved.
First off, the model:
class Post(models.Model):
image = models.ForeignKey(Image, null=True, blank=True)
title = models.CharField(max_length=200)
slug = models.CharField(max_length=200, unique=True, blank=True)
description = models.TextField(blank = True)
text = models.TextField()
def __str__(self):
return self.title
and our form
class PostForm(BaseModelForm):
new_image = forms.ImageField(required=False)
def clean(self):
return self.cleaned_data
class Meta:
model = Post
fields = ('title','text', 'image', 'new_image', description')
help_texts = {
'new_image': _('either upload a new image, or choose from the gallery')
}
so then our view where it gets processed
def post_new(request):
if request.method == "POST":
form = PostForm(request.POST, request.FILES)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.slug=slugify(post.title)
if form.cleaned_data.get('new_image'):
image = Image(title=post.title, description=post.description, image = request.FILES['new_image'])
image.save()
post.image = image
post.save()
return redirect('post_detail', pk=post.pk)
else:
form = PostForm()
return render(request, 'blog/post_edit.html', {'form': form})
Now what this should be doing is creating a new image object out of the form field, then saving it to the foreignkey constraint on the post model. But what it does is nothing, the post uploads fine with a blank image field, and the image never gets created. This makes me think the if statement for 'new_image' is resolving to False but I can't figure out why it would do that.
Also, for conventions sake, should this be happening in the form class or the view class?
also, my image model
class Image(models.Model):
title = models.CharField(max_length=200)
description = models.TextField()
image = models.ImageField(upload_to = 'images/', default = 'images/None/no-img.jpg')
def __str__(self):
return self.title
You need to change
form = PostForm(request.POST)
to:
form = PostForm(request.POST, request.FILES)
you need to add request.FILES in order to upload the file as per the documentation
Django Documentation: File Uploads
You may want to use pre-save/post-save signals to create the new image save when a post/image is saved via the form to the Image model.
You need to add your Image model to this example. Looking at how you are saving the image information via the form, Im going to assume you aren't using the
ImageField()
model on your Image Model. Only way to tell is you showing that model as well.
Django Documentation: Model Field Reference: ImageField()
This field is how Django uploads images through forms.
You may need to add two forms to your view one for the post and the other for the new image upload.
Im still learning this myself so you can look on here and find some assistance with this.
The form template need this: enctype="multipart/form-data", I added that and suddenly we have functional image processing!
I have an app that let you create a profile.
One of my app feature is it's let you edit your name and upload a image.
The problem is the user cannot submit the image unless he type his name.How can I fix this page to make it so If the user submit an image but doesn't submit a name . He will still have his old name
or if he doesn't submit an image and changes his name . He will still have his old picture?
I tried adding blank=True and null = False , null = True but doesn't seem to do the job
My models.py
class Person(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=100,null=True,blank=False)
image = models.FileField(upload_to="images/")
def __unicode__(self):
return self.name
My forms.py
class PersonForm(forms.ModelForm):
class Meta:
model = Person
fields = ('image','name',)
My views.py
def Display(request):
if not request.user.is_authenticated():
return HttpResponseRedirect(reverse('world:LoginRequest'))
if request.method == 'POST':
form = PersonForm(request.POST, request.FILES)
if form.is_valid():
person = Person.objects.get(user=request.user)
person.image = form.cleaned_data['image']
person.name = form.cleaned_data['name']
person.save()
return render(request,'edit.html',{'form': PersonForm()})
class Person(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=100, blank=True)
image = models.FileField(upload_to="images/", blank=True)
def __unicode__(self):
return self.name
def Display(request):
if not request.user.is_authenticated():
return HttpResponseRedirect(reverse('world:LoginRequest'))
if request.method == 'POST':
form = PersonForm(request.POST, request.FILES)
if form.is_valid():
image = form.cleaned_data['image']
name = form.cleaned_data['name']
person = Person.objects.get(user=request.user)
if image:
person.image = form.cleaned_data['image']
if name:
person.name = form.cleaned_data['name']
person.save()
return render(request,'edit.html',{'form': PersonForm()})
django forms does validation on the users data
validation is done on two levels:
the field level:
if the field is required and the user didn't enter any data it will raise an error
if the user entered improperly formatted data for that field it will raise an error
you can override the behavior of field validation by implementing functions named as clean_fieldname
this type of validation results in form.field.errors
the form level:
checks for any (non-field specific) validation errors
you can override the behavior by implementing clean method
this type of validation results in form.non_field_errors
from your models:
image is not allowed to be blank which means it is required. not entering an image will raise an error
let blank=True for both name and image
also I recommend using form.save() over saving models from the views
also there is a built in ImageField for saving images