Django: How to upload an image to MEDIA_ROOT upon Model Creation - django

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">

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/'

Django Form not saving to database?

I am using CreateView to create entries on my frontend and then save to the database/backend. But each time i click save or submit the form redirect successfully as expected but will not save to the back end.
I have tried adding success_url to my views.py and also added get_absolute_url(self) to my models.py but still it hasn't worked.
Views.py
class Dashboard (LoginRequiredMixin, CreateView):
model = PostedJob
template_name ='accounts/dashboard.html'
fields = ['firstname', 'lastname', 'job_title', 'email', 'budget',
'country', 'state', 'address', 'job_category',
'description', 'photo']
success_message = "Redirect successfully created!"
login_url= 'login'
models.py
Type = (
('building inspection', 'Building Inspection'),
('agriculture', 'Agriculture'),
('construction', 'Construction'),
('maintenance & training', 'Maintenance & Training'),
('surveying & mapping', 'Surveying & Mapping'),
('events coverage', 'Events Coverage'),
)
class PostedJob(models.Model):
firstname=models.CharField(max_length=200)
lastname=models.CharField(max_length =150)
email=models.EmailField(max_length =150)
job_title= models.CharField(max_length =150)
budget=models.PositiveIntegerField()
country=models.CharField(max_length = 150)
state=models.CharField(max_length = 150)
address=models.CharField(max_length = 150)
job_category=models.CharField(choices=Type, default ='agriculture',
max_length=50 )
photo= models.ImageField(upload_to='/%Y/%m/%d/', blank=False,
null=False)
description=models.TextField(max_length = 1500)
post_date=models.DateTimeField(default = datetime.now, blank=True)
publish=models.BooleanField(default =False)
def __str__(self):
return self.job_title
def get_absolute_url(self):
return reverse('home')
urls.py
urlpatterns =[
path('accounts/dashboard', Dashboard.as_view(), name='dashboard'),
]
index.html
<form action="" method="post">
{% csrf_token %}
{{form.as_p}}
<div>
<input type="submit" id="submit" value="Submit" class="btn waves-
effect waves-grey green">
</div>
</form>
Also the form displays no error message. I will indeed be grateful for your help. Thanks.
The form shows no errors but using the following SQL queries in Python prompt
revealed the error :
from django.db import connection
cursor = connection.cursor()
error: django.core.exceptions.ImproperlyConfigured: Requested setting DATABASES, but settings are
not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call
settings.configure() before accessing settings.
** Updated**
However the environment variable does not really solve this. The problem is actually from the form itself. The form has an image field which is required by default. The form even though it redirect successfully on click, did not save to the database. The reason being that since all fields are required, the image will returning a blank field on every save or submit action causing the form to constantly not save on every submit action.
Solution: Simply add enctype="multipart/form-data to your form.
<form action="" method="POST" enctype="multipart/form-data">

Understanding Django and Django FormView

I am trying to create a Django web app that accepts text in a form/textbox, processes it and redirects to a webpage showing the processed text . I have written a half-functioning app and find de-bugging quite challenging because I don't understand most of what I've done. I'm hoping you will help me understand a few concepts, Linking to resources, also appreciated.
Consider this simple model:
class ThanksModel(models.Model):
thanks_text = models.CharField(max_length=200)
Is the only way to set the text of thanks_text through the manage.py shell? This feels like a pain if I just have one piece of text that I want to display. If I want to display a webpage that just says 'hi', do I still need to create a model?
Consider the view and template below:
views.py
class TestView(generic.FormView):
template_name = 'vader/test.html'
form_class = TestForm
success_url = '/thanks/'
test.html
<form action = "{% url 'vader:thanks'%}" method="post">
{% csrf_token %}
{{ form }}
<input type = "submit" value = "Submit">
</form>
I need to create another model, view and html template and update urls.py for '/thanks/' in order for the success_url to redirect correctly? (That's what I've done.) Do I need to use reverse() or reverse_lazy() the success_url in this situation?
Models are used when you are dealing with Objects and Data and DataBases that can contain a lot of information.
For Example A Person would be a model. their attributes would be age, name, nationality etc.
models.py
class Person(models.Model):
Name = models.CharField(max_length=50)
age = models.IntegerField()
nationality = models.CharField(max_length=50)
Thi deals with multiple bits of information for one object. (the object being the person)
A Thank you message would not need this? so scrap the model for the thank you message. just have views where you create the view using a templates and setting the view to a url.
views.py
class TestView(generic.FormView):
template_name = 'vader/test.html' # self explantory
form_class = TestForm # grabs the test form object
success_url = reverse_lazy('vader:thanks') # this makes sure you can use the name of the url instead of the path
def ThanksView(request): # its simple so you don't even need a class base view. a function view will do just fine.
return render(request,"thanks.html")
test.html
<form action = "{% url 'vader:thanks'%}" method="post">
{% csrf_token %}
{{ form }}
<input type = "submit" value = "Submit">
</form>
thanks.html
<h1>Thank you for Submitting</h1>
<h2> Come Again </h2>
url.py
from django.urls import path
from djangoapp5 import views
urlpatterns = [
path('', TestView.as_view(), name='test_form'),
path('thanks/', views.ThanksView, name='vader:thanks'),
]
I haven't tested this but hopefully it helps and guide you in the right direction

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})

Upload Image to Blob using GAE + Django

I'm writing an application using GAE and Django in which I want to give to user the ability to upload his image. Also, I want this image be stored as blob on GAE's datastore. I have seen many examples but nothing specific to this scenario. Although, I feel that is a common issue.
All I want, is to create a new product and this new product must have an image.
1st Attempt: I have tried to add an image attribute (db.BlobProperty()) in product's model, and obviously django does not include it on the presented form.
2nd Attempt: I have created a new entity with two attributes (product as db.ReferenceProperty() and image as db.BlobProperty()). With this I tried to work parallel with django form modifying the django HTML form (including an |input type='file' name='img' /|) expecting that I could take the image from the request object but I failed once more.
This is the Product Class:
class Product(db.Model):
id = db.IntegerProperty()
desc = db.StringProperty()
prodCateg = db.ReferenceProperty(ProductCategory)
price = db.FloatProperty()
details = db.StringProperty()
image = db.BlobProperty()
This is the Django Form (HTML):
<form action="{%url admin.editProduct product.key.id%}" enctype="multipart/form-data" method="post">
<table>
{{form}}
<tr><td><input type="file" name="img" /></td></tr>
<tr><td><input type="submit" value="Create or Edit Product"></td></tr>
</table>
</form>
This is the Django Form (python):
class ProductForm(djangoforms.ModelForm):
class Meta:
model = Product
exclude = ['id']
This is the request handler:
def editProduct(request, product_id):
user = users.GetCurrentUser()
#if user is None:
# return http.HttpResponseForbidden('You must be signed in to add or edit a gift')
product = None
if product_id:
product = Product.get(db.Key.from_path(Product.kind(), int(product_id)))
if product is None:
return http.HttpResponseNotFound('No product exists with that key (%r)' %
product)
form = ProductForm(data=request.POST or None, instance=product)
##########################
# Ambitious undertaking! #
##########################
#if not product_id:
# uploadedImage = get("img")
# photo = Image()
# photo.product = product
# uploadedPhoto = request.FILES['img'].read()
# photo.image = db.Blob(uploadedPhoto)
# image.put()
if not request.POST:
return respond(request, user, 'addprod', {'form': form, 'product': product})
errors = form.errors
if not errors:
try:
product = form.save(commit=False)
except ValueError, err:
errors['__all__'] = unicode(err)
if errors:
return respond(request, user, 'addprod', {'form': form, 'product': product})
product.put()
return http.HttpResponseRedirect('/product')
As you can see the request handler is based on the Google's Gift-Tutorial
So, If anyone could put his opinion I would be very thankful!
Thank you in advance!
You may want to look at an example that uses the blobstore API with blobstoreuploadhandler and/or edit your request handler to store the uploaded file as a blobproperty or a blobreferenceproperty depending on if you use the blobstore API or just a blobproperty variable. You can be specific about http post data i.e. `
self.request.post('img').file.read()
I do recommend that you choose the blobstore API and blobstoreuploadhandler since that will do some very good things for you automatically: 1. storing MIM type and 2. storing filename 3. enabling serving via get_serving_url that has several advantages.