Customize ImageField.image.url - django

I have a Django model for a user-uploaded photo. It contains a field defined as
class Photo(models.Model):
image = models.ImageField(upload_to='photo_image_files')
#property
def name(self):
return filename_without_extension
...
and settings.py defines
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')
This means images are saved in /media/photo_image_files/. I would like to have JPEG files accessible at /jpeg/:filename and hide the actual path to the images from visitors. I have defined the corresponding view and url. However, now {{ photo.image.url }} predictably points to /media/photo_image_files/filename.jpg. As a workaround, I include images as
<img src="/jpeg/{{ photo.name }}" />
But that means I specify the location twice, in urls.py and in the template. How can I customise the URL?

You could subclass ImageField and define your own .url() method:
class JpegImageField(ImageField):
#property
def url(self):
return "/jpeg/{name}".format(name=self.name)
and use the new field class instead of regular ImageField:
class Photo(models.Model):
image = models.JpegImageField(upload_to='photo_image_files')

Related

Django: Uploading Avatar Imagefile to media-Folder of Custom User Profile is not working

I'm currently trying to create a Custom User Model for being able to add a Avatar-Imagefield to ever User.
Therefore I've created a Model Profile with avatars as the directory (media/avatars/) for all Images:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
avatar = models.ImageField('Avatar', upload_to="avatars", default="avatars/profil_picture_icon.png")
I created the needed classes ProfileInline and UserAdmin:
class ProfileInline(admin.StackedInline):
model = Profile
can_delete = False
class UserAdmin(BaseUserAdmin):
inlines = (ProfileInline,)
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
I also defined the media-directory inside the settings:
MEDIA_ROOT = BASE_DIR / 'media'
MEDIA_URL = '/media/'
After that, I created a Form ProfileForm, where the User can upload the Image and a postsavereceiver to create a basic Profile-Model every time I'm creating a new User:
class ProfileForm(forms.ModelForm):
class Meta:
model = models.Profile
exclude = ('user',)
def post_save_receiver(sender, instance, created, **kwargs):
if created:
user_profile = models.Profile(user=instance)
user_profile.save()
post_save.connect(post_save_receiver, sender=settings.AUTH_USER_MODEL)
Inside my Template I then created a form with the Avatar-ImageField and a Save-Button to Upload that Image:
<form action="/profil/" method="post" id="avatar_form">
{% csrf_token %}
<img style="border-radius: 100px" id= "Profil_Image" src=" {{ user.profile.avatar.url }}">
{% load widget_tweaks %}
{{ profile_form.avatar|add_class:"profile_form" }}
<button id="update_button" style="left: 1210px; top: 385px" type="submit" form="avatar_form" name="avatar_update_btn" value="">Speichern</button>
</form>
Lastly inside my views.py the User can Update the Default-Image
elif 'avatar_update_btn' in request.POST:
profile_form = ProfileForm(request.POST, request.FILES)
models.Profile.objects.filter(user=request.user).update(avatar="avatars/" + profile_form.data['avatar'])
-> And here we got the Problem.
It's updating the Avatar-URL inside the Database with the correct Filename, but it can't find the Image after reloading the Page because the Image was not uploaded into the media/avatars/-Folder and I have no idea why.
As soon as I'm saving the Image over the admin-Page it's working perfectly and a new Image-Instance is created inside the avatars-Folder. But as soon as I'm trying to upload it with the ImageField, it's not creating a new Instance of the Image inside the Folder.
I think it must have something to do with the "update" - function of the model. Maybe with "update" it's just changing the URL without creating a new Image-Instance, that's why I've tried to upload it with the profile_form.save() - function. But it won't let me save it, since the save()-function wants to create a new Model-Instance but the Profile-Model is already existing.
Can you help me out here?
Thank you in Advance.
I believe the issue is with your settings.py. BASE_DIR is obviously the base directory of your project, where manage.py is located. Thus, your media folder should be in that directory, otherwise Django won't find it. And I take it you have a folder called avatar within the media folder. Try:
import os
BASE_DIR = os.path.dirname(os.path.abspath(__file__)) # You probably already have this
print(BASE_DIR) # Do this to check if your BASE_DIR is correct
...
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'

How to set image path in django template

I'm trying to display already stored database image in template. But here i'm getting only name of the image. How to solve this, Where i did mistake.
models.py
class Images(models.Model):
image = models.ImageField(upload_to='images', blank=True, null=True)
settings.py
STATIC_URL = '/static/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
urls.py
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
views.py
def display(request):
myimages_objs = Images.objects.all().values()
for i in myimages_objs :
myimages_objs = i['image']
return render(request, 'index.html', {'myimages_obj ': myimages_objs })
index.html
<div class="divi">
<img src="{{ myimages_obj }}" alt="image">
</div>
I don't understand why you are returning the objects inside the for loop, this causes only a single image to be displayed in the template. Instead of doing this, you can pass all the image objects in the template, then loop through the objects in the template to display the images.
You can change your view as:
def display(request):
myimages_objs = Images.objects.all() # get all the images
return render(request, 'index.html', {'myimages_objs':myimages_objs})
Then in your template:
<div class="divi">
{% for item in myimages_objs %} //loop through all the images
<img src="{{ item.image.url }}" alt="image">
{% endfor %}
</div>
Edit:
Change the upload_to to upload_to="images/".
As far as I am concerned, your images are not being stored inside the images folder as you have missed the backslash(which represents a folder name). So, the images are being saved inside the media folder.
But the image url is something like project_path/media/images/something.jpg which cannot be found as the images are not being saved inside the images folder.
Change the field as stated for new images to be stored in the specified folder. For old images, copy them to the images folder manually.
I hope this will help.

django cutom image name on upload

I need create a image upload with django, the problem is, django always saving in a global project(called linkdump) folder, I want save it on the project folder(linktracker).
setting.py:
STATIC_URL = '/static/'
model:
class Link(models.Model):
link_description = models.CharField(max_length=200)
link_url = models.CharField(max_length=200)
link_image = models.ImageField(upload_to= './static/')
def __str__(self):
return self.link_description
class Admin:
pass
now in the view:
<img src="{% static link.link_image %}" alt="{{ link.link_description }}">
it returns
http://127.0.0.1:8000/static/static/o-BLUE-LIGHT-SLEEP-facebook.jpg
and the upload is in the project folder(linkdump), not inside the app.
You can specify a function to return a custom path for the ImageField:
def get_upload_path(instance, filename):
return 'your/custom/path/here'
class Link(models.Model):
. . .
link_image = models.ImageField(upload_to=get_upload_path)
Now you can use information from the model instance to build up the path to upload to.
Additionally, you don't want to use the {% static %} template tag to specify the path to the image. You would just use the .url property of the field:
<img src="{{ link.link_image.url }}" alt="{{ link.link_description }}" />

Prevent users from seeing other user's uploaded pictures

I have a django applications that uses multi-users. Users can upload pictures on the system.I have created a picture model that has a foreignKey of a users to know which user uploaded which picture.
class Picture(models.Model):
picture = models.ImageField(upload_to='pictures')
uploader = models.ForeignKey(User)
#other specific fields like date etc
I have set up my settings file to use the MEDIA_URL and MEDIA_ROOT
settings.py
MEDIA_URL ='/media/'
MEDIA_ROOT = 'path/to/media/'
So I am able to access a picture in www.mydomain.com/media/pictures/picture.jpg. But I guess having a common MEDIA_ROOT means that any user can see this picture right?Not just the user who uploaded it. In my webpages I use
<img src={{image.picture}}>
to show images of a user. How can I prevent a user from seeing a picture a different user has uploaded(a forbidden message)? Can this be done on setup?
Your view function probably looks something like this:
#login_required
def theview(request):
...
image = Picture.objects.get(*args)
...
return render(request, 'template', {'image': image})
The point being that you can easily only pass the image along if it's the logged in user that have uploaded it. For instance
image = get_object_or_404(Picture, uploader=request.user, other_filters)
Or even
image = Picture.objects.get(*args)
image = image if image.uploader == request.user else None
Then in the django template
{% if image %}
<img ....>
{% endif %}
You can try like this:
in views:
def allpicture(request):
.....
#for all pictures
images= Picture.objects.filter(uploader=request.User)
return render(request, '/sometemplate', {'images': images})
def onepicture(request, pic_id):
....
#for one picture
image= Picture.objects.filter(id= pic_id, uploader=request.User) #url for this view should be like url(r'^pictures/(?P<pic_id>\d+)/$'
render render(request, '/sometemplate', {'image': image})

Django - accessing url of an imageField that corresponds to media_url not media_root

I have the following model containing an image field that I want to show up in my template show_item.html.
Models.py
class Item(models.Model):
def get_file(self, filename):
url = "%s/%s" % (settings.MEDIA_ROOT, filename)
return url
seller = models.ForeignKey(settings.AUTH_USER_MODEL)
pic1 = ImageField(blank=True, upload_to=get_file, max_length=500)
I know that MEDIA_ROOT and MEDIA_URL are properly configured.
The problem is that in my template, I try to access
<img src="{{ item.pic1 }}">
which fails to be found. Upon further investigation, this is because the path is showing up as the one in my file directory (e.g. /home/user/apps/media/filename) rather than the media_url (e.g. www.mysite.com/media/filename). I checked both locations and the image is in fact in both.
My question -- how do I access the url of the form www.mysite.com/media/filename in the template? I've tried {{ item.pic1.url }} as well, but it's still the path on my system.
Thanks!
If your image uploaded path can be accessed by the static path, just use the static url.
Or else I think you may write a url, pointing to a view to return the image response.
For example:
# in your urls.py
urlpatterns = patterns('',
url(r'^image/(?P<pk>\d+)/$', 'image_view', name='image_view'),
)
and then response the image content in the image view.
from django.core.servers.basehttp import FileWrapper
from django.http import StreamingHttpResponse
def image_view(request, pk):
item = Item.objects.get(pk=pk)
filename = item.pic1.url
wrapper = FileWrapper(open(filename, 'rb'))
response = StreamingHttpResponse(wrapper)
response['Content-Length'] = os.path.getsize(filename)
response['Content-Disposition'] = 'attachment; filename=image.jpg'
return response