Note: There are a lot of questions related to this on Stack Overflow. However, this is not duplicate, as it requests noob level explanation (i.e. someone who has completed the Django tutorial).
So I completed the Django tutorial and am off to make my own site to reinforce what I learned (and augment it as well). I wanted to make a site which could display some images of a given object. Since a lot of the before mentioned posts use goods and pictures (e.g. to emulate multiple images for a product on Amazon), lets go with that for this post as well.
I made myself an app, and am using name-spaced urls and templates (as described in the tutorial).
In addition, following the Many-to-One documentation, I have two models Product and ProductImage with the latter having a ForeignKey to the former.
class Product(models.Model):
...
class ProductImage(models.Model):
product = models.ForeignKey("Product", on_delete=models.CASCADE)
image = models.ImageField(upload_to=product_dir_path)
Using a reverse many-to-one monitor (e.g. aSpecificProduct.productimage_set.all() ) I grabbed all my images which I set up in a directory under the app's directory <my_app>/uploads/product_<id>/image_<id> using the little upload_to trick in the model field reference part of the documentation.
def product_dir_path(instance, filename):
return "<my_app>/uploads/product_{0}/{1}".format(instance.product.id, filename)
Great, so I am all ready to make a view that grabs a specific item, uses the above semantics to get all the images for that item, and use a template view to display those images!
So I put these images into a variable img_list and put it into my context, which is passed to render as an argument.
I have a loop set up and inside that loop I have
<img src="{{img.image.url}}"/>
which when inspecting the website I see
<img src="<my_app>/uploads/<specific_product>/image_<number>.png"/>
awesome!... except for the 404 error.
Now a lot of these other posts talk about setting MEDIA, MEDIA_ROOT, STATIC, in settings.py and adding something to the urlpatterns (but not specifying if it is the project urls.py or the app one...).
There is some other text explaining this, but waaaaay too technical for someone who just completed the django tutorial and has no idea what any of these things do, or why they are needed. To be fair, the ModelField reference for ImageField and FileField do not make it that clear either (at least to me) as to how one would do this (whereas the documentation for Many to One has plenty of examples to figure it out).
So, please, in a tutorial like fashion, what am I missing to getting my images to display? Please be very explicit as to what files are being changes (e.g. my_site/urls.py vs my_app/urls.py), what these changes do, and why they are needed. Given the host of actual duplicates, it is clear that figuring this out is not!
Thanks
EDIT:
I moved the where the file "uploads" to be under <my_app>/static/<my_app> and renamed it to product_images. Following the linked page on static files I tried the following. Hardcoding the link works. Using the solution by most of the other posts, does not.
<img src="{% static '<my_app>/product_images/product_<id>/<img_file>' %}"/>
works but
<img src="{{img.image.url}}"/>
does not where the latter has src="<my_app>/static/<my_app>/product_images/product_<id>/<img_file>"
and the former static/<my_app>/product_images/product_<id>/<img_file>
make certain changes to your mysite/urls.py
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
]
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL,
document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
settings.py file
STATIC_URL = '/static/'
MEDIA_URL = '/media/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]
STATIC_ROOT = os.path.join(os.path.dirname(BASE_DIR), 'static_cdn')
MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR), 'media_cdn')
Related
I'm porting a web app from PHP (Codeigniter) to Python (powered by Django.) I already migrated the database and am now using Django models. I populated the database with images using Django's ImageField field and the images are in its proper folders under MEDIA_ROOT.
I'm in development so I'm using Django's server to test the web app as I go and I can serve the images through image.url.
image.url serves the image from:
MEDIA_ROOT/folder/subfolder/numberImage_folder_subfolder.jpg
But I need it to be served from MEDIA_ROOT/numberImage_folder_subfolder.jpg.
In production I know I can serve the image using nginx's XSendFile or something like that. Django's server (runserver command) is very easy to use and gives me what I need at the moment. While in development, how can I deliver the image from the URL I require? Preferably using Django's server...
Any help will be so much appreciated.
I'm not entirely clear on what your URLs are and how they map to file paths (and don't have the reputation to ask for clarification yet). So correct me if I've understood it wrong.
But in the meantime, to rephrase the question slightly, I think you're saying that you have the files stored on disk in those subfolders like MEDIA_ROOT/folder/subfolder/numberImage_folder_subfolder.jpg, but when you actually want the URLs served, you don't want the folders in the URL, so the URL will have MEDIA_ROOT/numberImage_folder_subfolder.jpg? And only with the ./manage.py runserver?
Going ahead on those assumptions, we have two steps: map the URLs we want back to a file path, and then also make sure we're generating the URLs we want.
For the first, normally urls.py will have something like this to serve media files in development:
from django.conf.urls.static import static
if settings.DEBUG is True:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
which is basically adding url(r'^media/(?P<path>.*)$', django.views.static.serve) to your urlconf, where serve takes all paths below settings.MEDIA_URL and serves up the equivalent file under MEDIA_ROOT.
The simplest way to do this would be to create a custom function modelled on that same django.conf.urls.static which takes the path and adds the folders back in before passing it on to django.views.static.serve.
For the second, if you want the URLs generated from the ImageField.url to differ from the underlying file storage path, you'll need to have a custom ImageField subclass overriding the FileField.url property (or a custom Storage class that does a similar thing).
Like so:
class CustomURLImageField(django.forms.fields.ImageField):
def _get_url(self):
"""
Override the standard FileField.url property to mangle the URL from
MEDIA_ROOT/folder/subfolder/numberImage.jpg
to
MEDIA_ROOT/numberImage_folder_subfolder.jpg
"""
self._require_file()
#return self.storage.url(self.name)
path_components = self.name.split('/')
folder = path_components[-3]
subfolder = path_components[-2]
numberImage, ext = os.path.splitext(path_components[-1])
return '/{numberImage}_{folder}_{subfolder}{ext}'.format(**locals())
url = property(_get_url)
This will be likely true in production too... how are you generating your URLs?)
I have a question regarding Django. I created a site and everything works as it is indented to work. The only problem I have is that first of all my urls.py and views.py files are getting quite bloated (I have one method for every page I have) and that i have for every site one template. I use {% extend basetemplate.html %} for making it at least a bit generic. However I find this attempt not really nice. Creating a method inside the urls.py and views.py in addition to create a template html file seems the wrong attempt.
I already thought about building a big controller and did some googleing but i could not find what i was looking for.
Is there something like a best practice to achieve that? How do you guys handle the amount of templates?
Any advice would be more than welcome :)
One solution is to refactor your one application into multiple applications inside your project. Each would have its own urls.py & views.py.
If all your view does is render a template, then you can use TemplateView directly in your urls.py, and you don't need to define a view for each url.
from django.conf.urls import url
from django.views.generic import TemplateView
urlpatterns = [
url(r'^about/', TemplateView.as_view(template_name="about.html"), name="about"),
url(r'^contact/', TemplateView.as_view(template_name="contact.html"), name="contact"),
]
Other generic class based views might be useful as well. If there was a way to map the urls to the template names programatically (e.g. the template name was <slug>.html, you might not even need a url for each template. However, as I said in the comments above, I can't make any specific suggestions without seeing more of your code.
Create a directory called views with the following structure.
.../project/views/
.../project/views/__init__.py
.../project/views/feature_one.py
.../project/views/feature_two.py
.../project/views/feature_three.py
# in .../project/views/__init__.py import the others
from .feature_one import *
from .feature_tow import *
from .feature_threee import *
Now you can go on and import like before from your views. You can do the same thing for admin, models, form ..etc.
For templates, break them up, use inclusion tag to include the smaller partial files. Group them and create a nice directory structure.
Now I'm developing a feedback application,
So, the admin must see messages from users, filter them(read/unread) and mark them as important.
I have already done all the functionality I need, but i cannot customize my headers.
For example.
There is a default header in change list(see the screenshot, this header is selected),
How can I delete these headers or customize them?
Great thanks in advance.
Actually, the answer from #mayankTUM is not correct. It does not follow the django design philosophies and should not be implemented (#mayankTUM himself mentions one of the problems of his solution however there are many, many more)!
Basically, what you need to do can be done by overriding the admin templates. Because there are some problems with that (I will explain later), here's exactly what
I did to solve your requirement:
Created a directory named admin in my templates folder.
Copied there the change_list.html template from <django>\contrib\admin\templates\admin\change_list.html.
Added the following to the end of the new change_list.html: {% block content_title %}Hello world!{% endblock %}
Now, instead of "Select ... to change" it will print "Hello world!"
I have to notice that copying the whole change_list.html is not DRY - it'd be much better if I just created the file, made it extend from admin/change_list.html and add the content_title. However this is not working and will lead to infinite recursion (please check this bug report https://code.djangoproject.com/ticket/15053 ) -- this is the problem to which I was referring before. A better solution that copying over the whole template is discussed in the following questions:
Django: Overriding AND extending an app template
and
How to override and extend basic Django admin templates? and
django override admin template
PS: My TEMPLATE_DIRS and TEMPLATE_LOADERS project settings are these:
TEMPLATE_DIRS = (
PROJECT_PATH.child('templates'),
)
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
)
Edit: Some random edits seem to do the work, but I don't understand how.
I have just "installed" the app django-avatar into my django project. (currently, it is under development, so I am using the server which comes with django)
However. after I upload an avatar, neither can I find the avatar in any folder, nor is it being displayed. I am new to django, and python, so I don't know what to do, or which part of my code to post. I am completely clueless. I have set up a url, at which static files are server(via django.views.static.serve)
url(r'^media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT}),
(settings is imported)
However. I feel this is wrong. I would like some guiding.
Media/Static related settings:
MEDIA_ROOT = 'd:/a4m/media/'
MEDIA_URL = '/media/'
STATIC_ROOT = ''
STATIC_URL = '/static/'
STATICFILES_DIRS = (('d:/a4m/static/'),)
As for templates, I am just using the ones which django-avatar provides.
First, don't add your project's static directory to STATICFILES_DIRS. You shouldn't be manually putting anything in that directory, so there's no reason to serve it in development (which is why Django doesn't by default). Also, I don't think it's related to this issue, but STATIC_ROOT needs a real value, which should be in your case:
STATIC_ROOT = 'd:/a4m/static/'
That said, two things from looking at the django-avatar source:
It uses a setting named AVATAR_STORAGE_DIR, so you should probably define that if you haven't. It seems the following would be sensible:
AVATAR_STORAGE_DIR = MEDIA_ROOT + 'avatars/'
If, for whatever reason, the avatar can't be written to the filesystem, django-avatar returns silently. So, I would check things like permissions to ensure that Django will have no problems writing to any directory under AVATAR_STORAGE_DIR.
Hopefully, that will be enough to get you a further in debugging this issue.
What does Django do with MEDIA_ROOT exactly? I never understood it. Since Django itself doesn't serve static media, and you have to set up apache or something similar for it, why does it care in which directory it sits?
You're not the only one who wonders; check out Django ticket #10650. Based on the comments by Django developers there, I think this pretty much sums up what MEDIA_ROOT is used for:
Unfortunately, Django is also at fault for being far too vague in its docs about what
MEDIA_ROOT and MEDIA_URL are used for. Searching through Django's code confirms that
MEDIA_ROOT & MEDIA_URL are used for file upload, and that MEDIA_URL is provided as a
convenience to templates via the default context processors (available when using
RequestContext??).
It appears to be used by some classes in Django as a default -- for instance, FileSystemStorage uses it as the default place to store files.
When you creates a model with ImageField or FileField attributes, you should pass the upload_to argument. That is a relative path will be appended to your MEDIA_ROOT path and there will be save and retrieve that files.