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})
Related
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/'
So I originally had the user create a post in admin and be able to leave image field blank and Django would set a default image.
My problem is;
If a user uploads an image, then deletes the image in admin, I get an error: The 'image' attribute has no file associated with it. when accessing solo.html in browser.
How can I make the default image reappear and specifically come from static folder?
My code:
settings.py
STATIC_URL = '/static/'
STATIC_DIRS = os.path.join(BASE_DIR, 'static')
models.py
# FIXME: If default image is changed to user's upload but then deleted. Make default image reappear.
# Recipe Field
class Recipe(models.Model):
title = models.CharField(max_length=200)
image = models.ImageField(upload_to='recipes/images/', blank=True)
def get_image(self):
if not self.image:
return f'{settings.STATIC_URL}default.png'
return self.image.url
solo.html
<h2>{{ recipe.title }}</h2>
<h3>{{ recipe.category }}</h3>
<h3>{{ recipe.meal }}</h3>
<img src="{{ recipe.image.url }}">
I'm just starting with Django so I apologize in advance if it's all a mess.
Thank you in advance!
What's happening is that the file itself is being deleted, but recipe.image is not being set to None.
I have the following model with an ImageField() that extends User:
class Tenant(models.Model):
user = models.OneToOneField(User)
photo = models.ImageField()
apartment = models.ForeignKey('Apartment')
However, when I create the model from a form, the image does not save to my MEDIA_ROOT directory. This seems like very basic functionality but I can't find a straightforward answer anywhere.
When I create a User in the admin section the file saves but not when I create it with my form. What do I do?
The problem was in the view.
First I had:
...
photo = request.POST['photo'] # <--- 'POST', incorrect
tenant = Tenant(user=user, photo=photo, apartment=apartment)
tenant.save()
But corrected it to:
photo = request.FILES['photo'] # <--- 'FILES', correct
tenant = Tenant(user=user, photo=photo, apartment=apartment)
tenant.save()
From https://docs.djangoproject.com/en/1.8/topics/http/file-uploads/
Note:
I also had to change my form tag in my template from
<form action="/account/create_account/" method="post">
to
<form action="/account/create_account/" method="post" enctype="multipart/form-data">
hey guys I have a specific issue.
I have a view:
class Upload(CreateView):
model = Banner
fields = ["image"]
success_url = '/url/'
def form_valid(self, form):
#some specifics operations...
This image are uploading to /upload
And in other place (e.g. /other_place, image in this dict are not uploading) I have a directory with other images. I get path to this images:
for file in glob.glob("*.jpg"):
l.append(file)
And in template:
{% for i in l %}
<img src="{{MEDIA_URL}}other_place/{i}}">
{%endfor%}
And I have nice template with all of this image. But now I need to upload selected image. specifically I need to call Upload view to upload selected image. Do you know what I mean? :D
This is my models.py:
class UserImages(models.Model):
user = models.ForeignKey(User)
photo = models.ImageField(upload_to=get_file_path)
and this is my view which uploads images:
def uploadImageView(request):
if request.method == 'POST':
form = UploadImageForm(request.POST, request.FILES)
if form.is_valid():
instance = form.save(commit=False)
instance.user = request.user
instance.save()
return redirect('/')
else:
form = UploadImageForm()
return render(request, 'image.html', {'form': form})
and this is my view which displays user's images:
def displayImageView(request):
user = request.user
img = UserImages.objects.filter(Q(user__username=user))
return render(request, "displayImage.html", {"img": img})
and this is my displayImage.html template:
{% load staticfiles %}
{% for image in img %}
<img src="{% static image.photo.url %}" alt="" />
{% endfor %}
and when I go to the URL which loads the template, it displays four images. I remember that while testing the uploadImageView, I uploaded 4 images for the viewer. The location Django saves the images was
/home/images/static
and I went to that folder and deleted two images. I then refreshed the template page and it still displayed 4 images rather than two, so then I figured that I had to delete it from the actual database rather than the folder. I then did
python manage.py shell
>>> from app.models import UserImages
>>> from django.contrib.auth.models import User
>>> a = User.objects.get(username='testUser')
>>> b = UserImages(user=a)
>>> b
<UserImages: UserImages object>
>>> b.photo
<ImageFieldFile: None>
amd as you can see, only one ImageFieldFile shows up for the user who I uploaded 4 images for. How come I can only see one?
EDIT:
my UploadImageForm() is just this:
class UploadImageForm(forms.ModelForm):
class Meta:
model = UserImages
fields = ['photo']
Your problem is here:
>>> b = UserImages(user=a)
>>> b
<UserImages: UserImages object>
>>> b.photo
<ImageFieldFile: None>
This code snippet is creating a new instance of UserImages, setting the user attribute to the object a. It is not searching the database. Since you haven't attached any images to this new instance, b is None.
To search, you need to do this instead:
>>> b = UserImages.objects.filter(user=a)
You also shouldn't upload anything to the same folder that is pointed to by STATICFILES_DIRS, as this folder is managed by django and your files here will be overwritten. I hope /home/images/static isn't listed here.
User uploads are saved in a subdirectory pointed to by MEDIA_FILES and accessed using MEDIA_URL.
When you save images in the database they will be saved in there as well as in a new location in your /static/ directory, etc.. Usually Django attaches a image_1.jpg for example if the image was originally image.jpg.
Do your images have a many-to-many relationship to the User model? Earlier, you said that there were 4 images saved to the User, then you said 1. Your UserImages model has one field, so possibly you are not looping through it correctly in the terminal shell in order to check all images. Perhaps it needs to be b.photos.all() if b = UserImages(user=a) or something to that extent?