Django Form Instance not showing the Uploaded Image - django

I am making a CMS platform in Django. I want to create the EDIT Post method so that anyone can edit their post. The main problem is the ImageField. As ImageField is required in Django's Models.py.So, while creating edit Post method for the user the ImageField, the image which was uploaded at the time of post creation, is empty.
The image is available in the Post models (Database) But not showing on EDIT Post Page. Except for Image, every other field are editable and visible.
Here is Edit Post View
def updatePost(request,pk):
getEditPost= get_object_or_404(Post,pk=pk)
if request.method=="POST":
form = CkEditorForm(request.POST,request.FILES,instance=getEditPost)
try:
if form.is_valid():
form.save()
except Exception as e:
print(e)
else:
form = CkEditorForm(instance=getEditPost)
context= {
'myForm':form,
'post':getEditPost,
}
return render(request,"post/editPost.html",context)
Here is my forms.py
class CkEditorForm(ModelForm):
.....
.....
featuredImage = forms.ImageField(required=True)
My models.py
class Post(models.Model):
.....
.....
featuredImage = models.ImageField(upload_to="featured_image/")

Have you added MEDIA_URL and MEDIA_ROOT in settings.py file

I'm not sure if there is any way to load an image in the image field from the backend. But you can simply view the image on the edit page so that users can understand which image was uploaded previously. (Leaving the image field blank on the edit page won't affect the image field in the database.)
Do these changes to load an image on edit page:
views.py
# Create your views here.
def updatePost(request,pk):
getEditPost= get_object_or_404(Post,pk=pk)
if request.method=="POST":
form = CkEditorForm(request.POST,request.FILES,instance=getEditPost)
try:
if form.is_valid():
form.save()
except Exception as e:
print(e)
else:
form = CkEditorForm(instance=getEditPost)
context= {
'myForm':form,
'post':getEditPost,
# add this line
'feature_image': getEditPost.featuredImage,
}
return render(request,"post/editPost.html",context)
editPost.html
...
<!-- Before closing the blody tag -->
<script>
let image_field = document.getElementById('id_featuredImage') // form > image field
let load_image = document.createElement('img')
// Add media root before image link, in this case I'm adding /media/
load_image.setAttribute('src', "/media/{{feature_image}}")
load_image.setAttribute('alt', "Feature image")
image_field.parentNode.insertBefore(load_image, image_field.nextSibling)
</script>

i had a similar issue and the solution for me was this.
If you had the model form with a "forms.FileInput" widget, delete this widget.
So from this:
class UserInfoForm(ModelForm):
tags = forms.ModelMultipleChoiceField(queryset = Tag.objects.all(),widget = forms.CheckboxSelectMultiple())
class Meta:
model = UserInfo
widgets = {
'bio' : forms.Textarea(attrs={'class':'form-control mb-3', 'rows':'4', 'cols':'50'}),
'profilePhoto' : forms.FileInput(attrs={'class':'form-control mb-3'})
}
To this:
class UserInfoForm(ModelForm):
tags = forms.ModelMultipleChoiceField(queryset = Tag.objects.all(),widget = forms.CheckboxSelectMultiple())
class Meta:
model = UserInfo
widgets = {
'bio' : forms.Textarea(attrs={'class':'form-control mb-3', 'rows':'4', 'cols':'50'}),
}
With the widget you won't get the "Profilephoto:currently:" part
Now I have to figure out how to custom the classes of the file input, but for now I'm good like this

Related

Dropzone with django image upload

Im trying to upload images using Dropzone.js .
There doesnt seem to be any current tutorials for Dropzone although i used this link: https://amatellanes.wordpress.com/2013/11/05/dropzonejs-django-how-to-build-a-file-upload-form/
Essentially what i want to do is , The user uploads multiple images . a hotel_id is attached to the images and stored in the hotelphotos table as a url which is unique each time .
MY code
Models.py
class Photo(models.Model):
hotel = models.ForeignKey(Hotels,on_delete=models.CASCADE)
path = models.FileField(upload_to='files/%Y/%M/%d)
forms.py
class PhotosForm(forms.ModelForm):
class Meta:
model = Photo
fields = ['path']
views.py
def uploadPhoto(request,hotelid):
if request.method == 'POST':
form = PhotosForm(request.POST,request.FILES)
if form.is_valid():
new_file = Photo(path = request.FILES['file'] , hotel_id = hotelid)
new_file.save()
else:
form = PhotosForm()
hotelid = hotelid
data = {'form': form, 'hotelid':hotelid}
return render(request, template , data)
The form
<form class="dropzone" action="{% url 'ManageHotels:uploadPhoto' hotelid %} method = "POST">
</form>
Files uploaded dont get created and the url also doesnt add to the database.
Hope someone can help!

Django get renamed uploaded image url

I am working on a project that will allow the user to upload image. The uploaded image will later on displayed and be passed to another form. To do this, I need to get the image url of the uploaded image. Here is my code:
def inputImage(request):
if request.method == 'POST':
form = ImageDetailsForm(request.POST, request.FILES)
if form.is_valid():
form.save()
message = "The image was successfully uploaded!"
imageName = str(request.FILES['image'].name)
imageURL = settings.MEDIA_URL + "/" + imageName
return render(request,'success.html', {'message': message, 'image': imageURL})
The code is working, however a problem would occur if the user uploads a file with an existing filename at the storage. To avoid conflict, Django automatically renames the file but the line
imageName = str(request.FILES['image'].name)
only returns the original filename of the uploaded image. I have also tried to use
imageName = str(form.cleaned_data['image'].name)
but still no changes. It returns "/media//1.png" instead of "/media//1_0rnKMaT.png"
Any ideas on how to get the URL of the current upload in Django?
Edit:
here is my models.py:
class ImageDetails(models.Model):
image = models.ImageField(null=True)
and my forms.py
class ImageDetailsForm(forms.ModelForm):
class Meta:
model = ImageDetails
fields= ('image')
widgets = {
'status': forms.HiddenInput(),
}
You can access the saved model instance through form.instance, so you should be able to get the name of the saved file with form.instance.image_field_name.name where image_field_name is the name of the image field in your model.

Django: How to prepopulate file upload path with current file path?

I have a modelform that my views generate an HTML form for editing content. Currently, it's able to pull in the current stored text content, like this:
#login_required
def edit_person(request, s_id, p_id):
p = get_object_or_404(Person, id=p_id)
if request.method == 'POST':
form = PersonForm(request.POST, request.FILES)
if form.is_valid():
p.name = request.POST['name']
p.title = request.POST['title']
handle_uploaded_file(request.FILES['photo'], request.FILES['photo'].name, 'media/images/people/')
p.photo = request.FILES['photo']
p.order = request.POST['order']
p.save()
return HttpResponseRedirect('/section/'+s_id+'/')
else:
return HttpResponse("error")
else:
form = PersonForm({ 'name': p.name, 'title': p.title, 'photo': p.photo, 'order': p.order })
return render_to_response('auth/edit-form.html', { 'form': form }, context_instance=RequestContext(request))
return HttpResponseRedirect('/section/'+s_id+'/')
However, the photo file path is blank. I don't want the user to have to upload a new file every time they edit something if they don't want to change the image. How do I get the file upload field to appear pre-populated and not overwrite itself if they don't change it? Thanks.
Believe it or not, it can be done! However, it requires the use of a custom django app called django-file-resubmit
Note that app as given only works for the widgets in admin and requires sorl-thumbnail.
You may prefer to use my fork:
https://github.com/JordanReiter/django-file-resubmit
It's a general-purpose version for use everywhere a ModelForm is used that doesn't have any other prerequisites.
It's pretty cool in that it automagically stores the file on submission (even if there is a validation error) and retrieves it from the cache when the widget is rendered in the form.
This is literally all you have to do to implement it:
import file_resubmit.widgets
class PersonForm:
""" existing code here """
photo = forms.ImageField(required=False, widget=file_resubmit.widgets.ResubmitImageWidget())

Cannot upload an image in django using ImageField

I am writing my first django app that uses the ImageField and I am having
difficulty. The problem is that my images are not uploading. I have looked at
as many examples that I can find. And I'm not sure what's going wrong here.
I am trying to verify that my photos are uploading by looking in the location
of the upload_to directory. When the form is displayed in the web page the
correct upload file button is displayed. When I hit submit, the code below executes,
but no images are ever uploaded.
Based on the upload_to, I am expecting to see images uploaded to see images under either:
myproject/photos or myproject/media/photos correct?
Am I doing anything obvious wrong here? How do I get my images to upload?
--------------settings.py-------------
MEDIA_ROOT = '/home/me/django_projects/myproject/media/'
MEDIA_URL = '/media/'
--------------model.py-------------
class Person(models.Model):
lastName = models.CharField(max_length=20)
firstName = models.CharField(max_length=20)
image = models.ImageField(upload_to='photos', blank=True, null=True)
# save executes but no image is saved.
# Because images are uploaded along with a new entry, I needed a special
# work around to get the self.id
def save(self):
for field in self._meta.fields:
if field.name == 'image':
if self.id is not None:
#save image now
field.upload_to = 'photos/%d' % self.id
else:
#Save image later
saveImageLast = True
super(Customer, self).save() # should create self.id if not already
if saveImageLast == True:
field.upload_to = 'photos/%d' % self.id
super(Customer, self).save()
print "save complete" #prints, but no image ...?
--------------forms.py-------------
class PersonForm(ModelForm):
class Meta:
model = Person
fields = ( 'lastName', 'firstName', 'image' )
from django documentation, i think this can help (in the past this helped me):
Firstly, in order to upload files, you'll need to make sure that your
element correctly defines the enctype as "multipart/form-data"
<form enctype="multipart/form-data" method="post" action="/foo/">
In your view where you create an instance of the form with post data, ensure you have provided request.FILES
form = PersonForm(request.POST, request.FILES)
This is a bit late, but 'upload_to' is not a method. It's an attribute that represents the relative path from your MEDIA_ROOT. If you want to save an image in the folder 'photos' with the filename self.id, you need to create a function at the top of your model class. For instance:
class Person(models.Model):
def file_path(instance):
return '/'.join(['photos', instance.id])
image = models.ImageField(upload_to=file_path)
Then when you are actually saving your image you would call:
person = Person(firstName='hey', lasteName='joe')
person.image.save(ImageObject.name, ImageObject)
More on the image file objects here.
More on upload_to here.

How do I create an inline editable many-to-many relationship

the situation
In my example I want to create a Page model with a many to many relationship with a content-blocks model.
A page has a title, slug, and main content block.
content blocks have a title and a content block.
What I can get:
Showing page.blocks in the admin form displays a multi select of content blocks
Creating an inline form for the content blocks on the page admin shows several selects with a + sign to add more
What I am trying to accomplish:
Full CRUD on content block on the page admin
Note: Due to the difficulty of my request, I'm beginning to believe the UX pattern im trying to accomplish is wrong. If I want a content creator to come in and create a page, pick some existing content blocks (ex: an existing sidebar content block), and then create a new custom block. I don't think i want him to have to jump all over the place to do this...
Related Question without solutions:
How do I use a TabularInline with editable fields on a ManyToMany relationship?
EDIT
my admin.py
from django.contrib import admin
from django.contrib.flatpages.admin import FlatpageForm, FlatPageAdmin
from django.contrib.flatpages.models import FlatPage
from my_flatpages.models import ExtendedFlatPage, ContentBlock
from mptt.admin import MPTTModelAdmin
from django import forms
import settings
"""
Extended Flatpage Form
"""
class ExtendedFlatPageForm(FlatpageForm):
class Meta:
model = ExtendedFlatPage
"""
Page Content Block inline form
"""
class ContentBlockInlineAdminForm(forms.ModelForm):
# Add form field for selecting an existing content block
content_block_choices = [('', 'New...')]
content_block_choices.extend([(c.id, c) for c in ContentBlock.objects.all()])
content_blocks = forms.ChoiceField(choices=content_block_choices, label='Content Block')
def __init(self, *args, **kwargs):
super(ContentBlockInlineAdminForm, self).__init__(*args, **kwargs)
# Show as existing content block if it already exists
if self.instance.pk:
self.fields['content_block'].initial = self.instance.pk
self.fields['title'].initial = ''
self.fields['content'].initial = ''
# Make title and content not required so user can opt to select existing content block
self.fields['title'].required = False
self.fields['content'].required = False
def clean(self):
content_block = self.cleaned_data.get('content_block')
title = self.cleaned_data.get('title')
content = self.cleaned_data.get('content')
# Validate that either user has selected existing content block or entered info for new content block
if not content_block and not title and not content:
raise forms.ValidationError('You must either select an existing content block or enter the title and content for a new content block')
"""
Content Block Inline Admin
"""
class ContentBlockInlineAdmin(admin.TabularInline):
form = ContentBlockInlineAdminForm
class Meta:
model = ContentBlock
extra = 1
"""
Extended Flatpage Admin
"""
class ExtendedFlatPageAdmin(FlatPageAdmin, MPTTModelAdmin):
form = ExtendedFlatPageForm
fieldsets = (
(
None,
{
'fields': ('url', 'title', 'content', ('parent', 'sites'))
}
),
(
'SEO Fields',
{
'fields': ('seo_title', 'seo_keywords', 'seo_description'),
'classes': ('collapse', )
}
),
(
'Advanced options',
{
'fields': ('enable_comments', 'registration_required', 'template_name'),
'classes': ('collapse', )
}
),
)
inlines = (ContentBlockInlineAdmin,)
class Media:
js = (
'https://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js',
settings.MEDIA_URL + 'js/tinymce/jquery.tinymce.js',
settings.MEDIA_URL + 'js/init_tinymce.js'
)
admin.site.unregister(FlatPage)
admin.site.register(ExtendedFlatPage, ExtendedFlatPageAdmin)
Haven't had the opportunity to test this, but it should work:
class ContentBlockInlineAdminForm(forms.ModelForm):
# Add form field for selecting an existing content block
content_block_choices = [('', 'New...')]
content_block_choices.extend([(c.id, c) for c in ContentBlock.objects.all()])
content_blocks = forms.ChoiceField(choices=content_block_choices, label='Content Block')
def __init(self, *args, **kwargs):
super(ContentBlockInlineAdminForm, self).__init__(*args, **kwargs)
# Show as existing content block if it already exists
if self.instance.pk:
self.fields['content_block'].initial = self.instance.pk
self.fields['title'].initial = ''
self.fields['content'].initial = ''
# Make title and content not required so user can opt to select existing content block
self.fields['title'].required = False
self.fields['content'].required = False
def clean(self):
content_block = self.cleaned_data.get('content_block')
title = self.cleaned_data.get('title')
content = self.cleaned_data.get('content')
# Validate that either user has selected existing content block or entered info for new content block
if not content_block and not title and not content:
raise forms.ValidationError('You must either select an existing content block or enter the title and content for a new content block')
class ContentBlockInlineAdmin(admin.TabularInline):
form = ContentBlockInlineAdminForm
class Meta:
model = ContentBlock
extra = 1
class PageAdmin(admin.ModelAdmin):
inlines = [
ContentBlockInlineAdmin,
]
"""
Override saving of formset so that if a form has an existing content block selected, it
sets the form instance to have the pk of that existing object (resulting in update rather
than create). Also need to set all the fields on ContentType so the update doesn't change
the existing obj.
"""
def save_formset(self, request, form, formset, change):
for form in formset:
if form.cleaned_data.get('content_block'):
content_block = ContentBlock.objects.get(pk=form.cleaned_data.get('content_block'))
instance = form.save(commit=False)
instance.pk = content_block.pk
instance.title = content_block.title
instance.content = content_block.content
instance.save()
else:
form.save()
You could then actually add some javascript to show/hide the ContentBlock fields depending on whether the content_block field is set to 'New..' or an existing one.
This isn't the answer I was looking for, BUT, What I ended up going with is
class Page(models.Model):
....
class ContentBlock(models.Model):
page = models.ForeignKey(
Page,
blank = True,
null = True,
)
....
and then having a regular tabular inline for ContentBlock on the page admin form.
So that way I can have page specific content blocks related to a page, AND be able to have generic content blocks able to be used wherever.
Then, I created an inclusion tag to render a content block by name that I use in my templates.
The project https://github.com/caktus/django-pagelets sounds like exactly what you are looking for. A page can have 'pagelets' and 'shared pagelets' with a nice admin for the two (pagelets are simply content blocks).
The non-shared pagelets are shown as inlines with the ability to add extra blocks directly on the page admin screen. For shared pagelets you get the drop-down with a plus-sign.