I'm quite new to using Django and I am trying to develop a website where the user can download file at the end of the post.
Post model:
class Article(models.Model):
title = models.CharField(max_length=30)
content = models.TextField()
pub_date = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
file = models.FileField(upload_to='code_files/')
def __str__(self):
return self.title
I have a views:
def download(request):
response = HttpResponse(open(f"media/code_files/tests.py", 'rb').read())
response['Content-Type'] = 'text/plain'
response['Content-Disposition'] = f'attachment; filename=tests.py'
return response
How can I download the file linked with the post?
From Dajngo docs FileResponse is a subclass of StreamingHttpResponse optimized for binary files.
import os
from django.http import FileResponse
from django.views.decorators.http import require_POST
#require_POST
def download(request):
article = Article.objects.get(id=1)
fullpath = article.file.path
if not os.path.exists(fullpath):
raise Http404('{0} does not exist'.format(fullpath))
return FileResponse(
open(fullpath, 'rb'), as_attachment=True,
filename=article.file.name)
Assuming that you passed the Article object to your template from your views as a context.
context = {
'article' = article_obj,
}
In your HTML, you need to use the embed tag and the url of the FileField of the Article object.
<embed src="{{article.file.url}}" width="500" height="200">
You shouldn't need to write another view just to download the file. Just have the article in your context in the view where you want to show the link to download:
context = {'article': Article.objects.first()}
Make sure that you have also added your media URL in your settings:
MEDIA_URL = "/media/"
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
and project urls.py:
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Then, for your article model, give each article a formatted file url:
class Article(models.Model):
#property
def formatted_file_url(self):
if self.file:
return settings.MEDIA_URL + self.file.url
else:
return 'No url'
Download with:
<a href="{{ article.formatted_file_url }}" download>
Related
Problem
I am able to upload images into S3, however the structure of the URL within my django app is not structured properly.
The URL appears like this: http://localhost:8000/plants/https://'%s.s3.amazonaws.com'%AWS_STORAGE_BUCKET_NAME/media/plant_images/image.jpeg
However, I want it to appear as https://opensprouts-dev-media.s3.amazonaws.com/media/plant_images/images.jpeg
Context
I have a model that allows me to upload multiple images. I think I need to rewrite the way I call for the image within my template, or within my model. After reading through Django documentation and other stackoverflow questions, I'm not sure how to resolve this issue.
settings.py/base.py
# MEDIA
# ------------------------------------------------------------------------------
USE_S3 = os.getenv('USE_S3') == 'TRUE'
AWS_ACCESS_KEY_ID = config('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = config('AWS_SECRET_ACCESS_KEY')
AWS_STORAGE_BUCKET_NAME = config('AWS_STORAGE_BUCKET_NAME')
AWS_QUERYSTRING_AUTH = config('AWS_QUERYSTRING_AUTH')
AWS_S3_CUSTOM_DOMAIN = config('AWS_S3_CUSTOM_DOMAIN')
AWS_S3_OBJECT_PARAMETERS = {
'CacheControl': 'max-age=86400',
}
AWS_LOCATION = config('AWS_LOCATION')
PUBLIC_MEDIA_LOCATION = 'media'
AWS_QUERYSTRING_AUTH = False
MEDIA_URL = 'https://%s/%s/' % (AWS_S3_CUSTOM_DOMAIN, AWS_LOCATIN)
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
The section of my template that is calling for the URL {{ image.images.url }}
{% for image in plant.plant_images.all %}
{% if forloop.first %}
<div class="carousel-item active">
{% else %}
<div class="carousel-item">
{% endif %}
<img src="{{ image.images.url }}" alt="{{ plant.name }}">
</div>
{% endfor %}
</div>
models.py
import datetime
from django.conf import settings
from django.db import models
from django.utils import timezone
from django.template.defaultfilters import slugify
from django.urls import reverse
from django_quill.fields import QuillField
# Create your models here.
class Plant(models.Model):
name = models.CharField(max_length=120)
slug = models.SlugField(null=False, unique=True)
description = models.TextField()
def __str__(self):
return self.name
def publish(self):
self.published_date = timezone.now()
self.save()
def get_absolute_url(self):
return reverse("plant_detail", kwargs={"slug": self.slug})
class PlantImage(models.Model):
plant = models.ForeignKey(Plant, default=None, on_delete=models.CASCADE, related_name="plant_images")
images = models.ImageField(upload_to = 'plant_images/')
def __str__(self):
return self.plant.name
views.py
from .models import Plant, PlantImage
def plant_index(request):
plant_objects = Plant.objects.all()
context = {'plant_objects': plant_objects}
return render(request, 'plants/plant_index.html', context)
class PlantDetailView(DetailView):
model = Plant
template_name = 'plants/plant_detail.html'
slug = 'slug'
def get_context_data(self, **kwargs):
context = super(PlantDetailView, self).get_context_data(**kwargs)
context['plant_images'] = PlantImage.objects.all()
return context
plant_detail = PlantDetailView.as_view()
I can see the images are successfully being stored in s3 after I upload them via my Django backend UI, however the URL is not allowing me to display the images on the page correctly.
You can use boto3 config to upload your images and that will not add http://localhost:8000/plants in your base url of image.
from storages.backends.s3boto import S3BotoStorage
class PublicMediaStorage(S3Boto3Storage):
location = "media"
default_acl = "public-read"
file_overwrite = False
custom_domain = False
and use the above class in your model using storage params of django model
class PlantImage(models.Model):
plant = models.ForeignKey(Plant, default=None, on_delete=models.CASCADE, related_name="plant_images")
images = models.ImageField(storage=PublicMediaStorage() ,upload_to = 'plant_images/')
def __str__(self):
return self.plant.name
I have simply model:
class SimplyModel(models.Model):
name = models.CharField(max_length=50)
image = models.ImageField(upload_to="images/", blank=True, null=True)
def get_image(self):
return self.image.path
def __str__(self):
return self.name
And I serilize it by
from django.core import serializers
def get_my_model(request):
#some operations...
data = serializers.serialize('json', SimplyModel.objects.all())
return HttpResponse(
data,
content_type="application/json"
)
But on frontend when I service this data, in image filed I have only:
images/myimage.jpg
without media prefix.
I have all 'media-configuration'
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, "../media")
And I can get this image by serveradress.com/media/images/myimage.jpg
I tried to add method to model, like
def get_image(self):
return self.image.path
But I cannot see this method in my respons.
How can I get this absolute path? Or serilize method by django-core serialzer? (I don't want to use DRF)
Best
You do not need to add a method to your model to get the url.
Just access it in your template by using the built in .url method.
Example of how it works:
obj = SimplyModel.objects.get(pk=123)
url = obj.image.url
I have defined a model with a 'pic' image field:
class Photo(models.Model):
title = models.CharField(max_length=100)
body = models.TextField()
teaser = models.TextField('teaser', blank=True)
pic = models.ImageField(upload_to = 'pic_folder/', default = 'pic_folder/None/default.jpg')
created=models.DateTimeField(default=datetime.datetime.now)
pub_date=models.DateTimeField(default=datetime.datetime.now)
categories = models.ManyToManyField(Category, blank=True)
likes = models.IntegerField(default=0)
dislikes = models.IntegerField(default=0)
visits = models.IntegerField(default=0)
slug = models.CharField(max_length=100, unique=True, blank=True)
And here is the upload form:
class PhotoForm(forms.ModelForm):
class Meta:
model= Photo
fields = ( 'title', 'pic','body', 'categories')
Wich post to this view:
#staff_member_required
def add_photo(request):
if request.method == 'POST':
form = PhotoForm(request.POST, request.FILES)
if form.is_valid():
form.save()
messages.info(request, "photo was added")
return render(request, 'home.html')
else:
messages.info(request, 'form not valid')
return render(request, 'home.html')
if request.method == 'GET':
form = PhotoForm()
args = {}
args.update(csrf(request))
args['form'] = form
return render(request, 'photo/add_photo.html', args)
The problem is that while photo objects are being saved and the file uploaded but the images are not displayed.
<div class="photo_body"><img src="pic_folder/someimage.jpg" ></div>
I also set
MEDIA_ROOT = '/path/to/my_project/pic_folder'
and run manage.py collectstatic, but they did not solve the problem.
I really got confused about this. So appreciate your hints.
First of all make a folder called media in your project's directory.
Django will store all your uploaded images inside media/pic_folder/ automatically.
In your settings.py file, add this:
MEDIA_URL = '/media/'
MEDIA_ROOT = '/path_to/media/' # write the path to the media folder you just created.
In your urls.py file, add the following lines at the top:
from django.conf import settings
from django.conf.urls.static import static
and add the following line at the end:
+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
something like below:
urlpatterns = patterns('',
# Your urls here
) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
And, finally, in your templates:
<img src="{{MEDIA_URL}}pic_folder/someimage.jpg" />
I'm been working on this Photo Organizer and Sharing App Part I at http://lightbird.net/dbe/photo.html. I'm trying to add a picture and when I do . I get this error.
Page not found (404)
Request Method: GET
Request URL: http://donkey123.pythonanywhere.com/admin/photo/image/1/images/Penguins_7.jpg/
image object with primary key u'1/images/Penguins_7.jpg' does not exist.
You're seeing this error because you have DEBUG = True in your Django settings file. Change that to False, and Django will display a standard 404 page.
My Models is
from django.db import models
from django.contrib.auth.models import User
from django.contrib import admin
from string import join
import os
from PIL import Image as PImage
from mysite.settings import MEDIA_ROOT
class Album(models.Model):
title = models.CharField(max_length=60)
public = models.BooleanField(default=False)
def __unicode__(self):
return self.title
class Tag(models.Model):
tag = models.CharField(max_length=50)
def __unicode__(self):
return self.tag
class Image(models.Model):
title = models.CharField(max_length=60, blank=True, null=True)
image = models.FileField(upload_to="images/")
tags = models.ManyToManyField(Tag, blank=True)
albums = models.ManyToManyField(Album, blank=True)
created = models.DateTimeField(auto_now_add=True)
rating = models.IntegerField(default=50)
width = models.IntegerField(blank=True, null=True)
height = models.IntegerField(blank=True, null=True)
user = models.ForeignKey(User, null=True, blank=True)
def __unicode__(self):
return self.image.name
def save(self, *args, **kwargs):
"""Save image dimensions."""
super(Image, self).save(*args, **kwargs)
im = PImage.open(os.path.join(MEDIA_ROOT, self.image.name))
self.width, self.height = im.size
super(Image, self).save(*args, ** kwargs)
def size(self):
"""Image size."""
return "%s x %s" % (self.width, self.height)
def __unicode__(self):
return self.image.name
def tags_(self):
lst = [x[1] for x in self.tags.values_list()]
return str(join(lst, ', '))
def albums_(self):
lst = [x[1] for x in self.albums.values_list()]
return str(join(lst, ', '))
def thumbnail(self):
return """<img border="0" alt="" src="/media/%s" height="40" />""" % (
(self.image.name, self.image.name))
thumbnail.allow_tags = True
class AlbumAdmin(admin.ModelAdmin):
search_fields = ["title"]
list_display = ["title"]
class TagAdmin(admin.ModelAdmin):
list_display = ["tag"]
class ImageAdmin(admin.ModelAdmin):
search_fields = ["title"]
list_display = ["__unicode__", "title", "user", "rating", "size", "tags_", "albums_","thumbnail","created"]
list_filter = ["tags", "albums","user"]
def save_model(self, request, obj, form, change):
obj.user = request.user
obj.save()
I think My Models,py is fine but I think the problem lay at the
MEDIA_ROOT= and MEDIA URL=
My MEDIA_ROOT = '/home/donkey123/mysite/photo'
and my MEDIA_URL is "" which is blank.
I don't know what to put for my MEDIA_URL which is the problem.
Thanks in advance
In your model (note there is no need for the slash at the end):
image = models.FileField(upload_to="images")
That means these images will go in {media folder}/images/
In settings.py:
MEDIA_ROOT = os.path.join(os.path.dirname(__file__), '../../web/media').replace('\\','/')
That means the media folder is: project_root/web/media
So the images from this model will go in: project_root/web/media/images
The stuffs above define where the images are saved on the filesystems. Now what you want to do is to define where they will be accessed via HTTP.
In settings.py define the url pointing to the media folder:
MEDIA_URL = '/media/'
Then the images from this model will be in:
http://my-url/media/images/image_name.jpg
Tests these settings without your model/library first with one image. Then read the following:
To reply to your specific questions:
My MEDIA_ROOT = '/home/donkey123/mystic/photo' => see my example, the MEDIA_ROOT should be relative to your settings.py file so it will work when going live, on another dev machine, etc.
and my MEDIA_URL= is "" which is blank. => that should be '/media/' because in the model you are doing this:
def thumbnail(self):
return """<a href="/media/%s">
Hope that helps
settings.py
import os
PROJECT_ROOT = os.path.join(os.path.dirname(__file__), '..')
SITE_ROOT = PROJECT_ROOT
MEDIA_ROOT = os.path.join(SITE_ROOT, 'media')
MEDIA_URL = '/media/'
Problem is with the regular expression in your urls.py, which is taking everything after 1 and passing it in as the primary key.
I think, When any web page is removed or moved then 404 happens. Otherwise your hosting provider is not good.
I am new to Django and trying to make a basic website and having a few difficulties getting the relevant record for a page.
Models.py
import datetime
from django.db import models
from django.conf import settings
from ckeditor.fields import RichTextField
from markdown import markdown
class LiveEntryManager(models.Manager):
def get_query_set(self):
return super(LiveEntryManager,self).get_query_set().filter(status=self.model.LIVE_STATUS)
class Page(models.Model):
LIVE_STATUS = 1
HIDDEN_STATUS = 2
STATUS_CHOICES = (
(LIVE_STATUS, 'Live'),
(HIDDEN_STATUS, 'Hidden'),
)
title = models.CharField(max_length=250, help_text='Max 250 characters.')
slug = models.SlugField(unique=True, help_text='Suggested automatically generated from the title. Must be unique')
description = RichTextField()
description_html = models.TextField(editable=False, blank=True)
status = models.IntegerField(choices=STATUS_CHOICES, default=LIVE_STATUS,
help_text="Only pages with live status will be publicly displayed")
def save(self, force_insert=False, force_update=False):
self.description_html = markdown(self.description)
super(Page, self).save(force_insert, force_update)
def get_record(self):
return self.objects.get()
#Managers
live = LiveEntryManager()
objects = models.Manager()
class Meta:
ordering = ['title']
verbose_name_plural = "Pages"
def __unicode__(self):
return self.title
def get_absolute_url(self):
return "/%s/" % self.slug
class Image(models.Model):
page = models.ForeignKey(Page)
name = models.CharField(max_length=250)
image = models.ImageField(upload_to='gallery')
class Meta:
ordering = ['name']
def __unicode__(self):
return self.name
And Views.py
from django.shortcuts import get_object_or_404, render_to_response
from django.views.generic.list_detail import object_list
from mainsite.models import Page, Image
def home(request):
return render_to_response('templates/home.html')
def page(request, slug):
one_page = get_object_or_404(Page, slug=slug)
return render_to_response('templates/page/page.html',
{ 'object_list': one_page.get_record() })
and Urls.py
urlpatterns = patterns('',
# Examples:
url(r'^$', 'mainsite.views.home', name='home'),
# url(r'^disability/', include('disability.foo.urls')),
url(r'^page/(?P<slug>[-\w]+)/$', 'mainsite.views.page'),
# Uncomment the admin/doc line below to enable admin documentation:
# url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^grappelli/', include('grappelli.urls')),
# Uncomment the next line to enable the admin:
url(r'^admin/', include(admin.site.urls)),
)
What I want to do is to be able to go to say /page/test and get the title, description and image for that object only. I know how to get all objects and display them in a template but cannot figure out how to get a single record and would be grateful of some help.
Thanks
If you would want to query the database for a single Page model you could use
views.py
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpResponse
from models import Pages
def page(request, slug):
try:
page = Pages.objects.get(slug=slug)
except ObjectDoesNotExist:
return HttpResponse(status=404)
return render(request, 'templates/page/page.html', {'page': page})
Then just use
{{page.title}}
or something like that in your template.
Also, if you want to query and have an iterable list returned intead you coule use
Pages.objects.filter()
instead of
Pages.objects.get()
Where'd you get get_record() from? The return value of get_object_or_404 is a model instance. There's nothing else to "get", just pass the instance to your context. And, don't use object_list as a context name. Not only is this an instance, not a list or queryset, but that name is most often used with pagination, and you'll simply create confusion in your code if you're not actually paginating anything.
return render_to_response('templates/page/page.html', {
'one_page': one_page,
})
Then in your template:
<h1>{{ one_page.title }}</h1>
<img src="{{ one_page.image.url }}" alt="">
<p>{{ one_page.description }}</p>