Django REST views: return images without models - django

I am completely new to Django REST, and I have been wasting time googling for a way to accomplish something that does not seem to be excessively difficult.
I want to define a view using the Django predefined classes for views. This view should return an image (given its name), so a URL like this would return the image example.png:
http://localhost:8000/api/v1/image/example.png
However I do not want this image to be stored in a model (it would be stored in the server as a png file). The class rest_framework.generics.RetrieveAPIView seems to suit my needs, as I can set permissions and authentication classes to restrict the access to the images, but I cannot figure out how to set the queryset if I do not have a model with my images.
Any ideas on this? Thanks and forgive my ignorance.

If I understand well, you want to check permissions before allowing access to an image.
I do not think Generic Views is the good path for you as you are not tied to a db model:
The generic views provided by REST framework allow you to quickly
build API views that map closely to your database models.
A better path could be Class Based Views, as suggested by Ivan Semochkin, something like:
class GetMyImage(APIView):
authentication_classes = (...,)
permission_classes = (...,)
def get(self, request, format=None):
with open('file path...') as fh:
return HttpResponse(fh.read(), content_type='content/image')
Of course do not forget your urls.py:
urlpatterns = [
url(r'^image/example.png$', views.GetMyImage.as_view()),
]

You can use HttpResponse to return simple json data. Please override get method in your view class.
import json
from django.http import HttpResponse
response_data = {}
response_data['image'] = 'http://localhost:8000/api/v1/image/example.png'
return HttpResponse(json.dumps(response_data), content_type="application/json")

Django will store all your media files where you have specified your root media folder to be. It will also serve them from the specified root media url.
In your settings add;
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')
MEDIA_URL = '/media/'
Images files will be then available at 'http://localhost:8000/media/' if in your root configuration urls you add:
urlpatterns = [
url(r'^api/v1/', include('api.urls', namespace='api')),
]+static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Remember to import your configuration settings and the static method:
from django.conf import settings
from django.conf.urls.static import static

Related

Simplest way to add a view to my Django admin UI?

What is the simplest way to add a view to my Django (3.2) admin UI? That is, add a URL mysite.com/admin/my-view so that visiting that URL acts like the rest of admin (in particular, requires similar permissions).
There is a whole page on this, but it's not obvious to me how to piece it together.
https://docs.djangoproject.com/en/3.2/ref/contrib/admin/#adding-views-to-admin-sites says you can add a get_urls to your AdminSite class. Okay, so I need my own AdminSite class. And I need to register it in apps, maybe?
I did this:
class MyAdminSite(admin.AdminSite):
def get_urls(self):
urls = super().get_urls()
my_urls = [
path('my-view', self.my_view, name='my-view')
]
return my_urls + urls
def my_view(self, request):
# do something ..
admin_site = MyAdminSite(name='my_admin')
and this in urls.py:
from .admin import admin_site
urlpatterns = [
path('admin/', admin_site.urls),
and this in unchanged in INSTALLED_APPS in settings:
'django.contrib.admin',
But, now it only shows the admin for the one app, instead of for all the apps. So how do I get it to auto-discover all the apps like it used to? Or is there a simpler way?
P.S. There is also a question on this, but the answer didn't have enough detail for me to use it.
EDIT: I'm reading Make new custom view at django admin and Django (1.10) override AdminSite ..
EDIT 2: This was a hack, but it works for me:
from django.contrib.admin import site
admin_site._registry.update(site._registry)
I had to update in the right place (the project urls.py), perhaps when all the other admin stuff is already loaded.

Is there any way to change the default path which Django will use to locate files in generic class based views?

So I am making a Blog website. I am using Class Bases Views to give the users an ability to add posts, view posts, update and delete them. So in my views.py, I have this:
from django.shortcuts import render, HttpResponse
from .models import Post
from django.views.generic import ListView, DetailView
# Create your views here.
def home(request):
context = {
'posts': Post.objects.all()
}
return render(request, 'index.html', context)
class PostListView(ListView):
model = Post
template_name = 'index.html'
context_object_name = 'posts'
ordering = ['-date_posted']
class PostDetailView(DetailView):
model = Post
My urls.py:
from django.urls import path
from . import views
from .views import PostListView, PostDetailView
urlpatterns = [
path('', PostListView.as_view(), name="home"),
path('post/<int:pk>/', PostDetailView.as_view(), name="post-detail"),
]
The problem is in the PostDetailView. When I create a file called post_detail.html in my templates folder, so that when user goes to a specific post in the website, we redirect them to the post_detail.html. But the site says that post_detail.html template cannot be found.
I have learnt that the the generic class based views will be looking for a template with this naming convention, <app>/<module>_<viewtype>.html. So it's going to be looking in the directory of the app name, which, in my case, is going to be "blog_app" then a template with a model name which is going to be "Post" and then the view type.
Is there any way to change the default path which Django will use to locate files in generic class based views?
So that I can then tell Django to look in my templates folder to search for post_detail.html template.
Note: I have registered my templates folder in the settings.py in the "dirs" like this 'DIRS': ['templates'],
Thanks
If you need to register your project’s templates directory folder then the setting that needs to be modified is DIRS inside TEMPLATES.
'DIRS': [os.path.join(BASE_DIR, 'templates')]
The BASE_DIR will already exist if you created your project using the default project template.
If APP_DIRS option is set to True in TEMPLATES, then
this is the relative path where your post_detail.html should be present.
blog_app/templates/blog_app/post_detail.html

Login Required Django Classed Based View

Totally beginner question.
I have this view in my app Foo:
class PaymentsView(LoginRequiredMixin, CreateView):
login_url = '/login/'
redirect_field_name = 'redirect_to'
# -- Omitted ...
I have read the django documentation but I didn't see where to put my file "login.html" and how can I set the url for that. When I run the code show the 404 error.
Setting login_url will redirect to a URL within your project, which is then handled by URL Dispatcher. You'll need to include a path to that URL within your URLConf, just like you would with any other URL:
urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('login/', views.login),
]
You'll then need to write a view to handle the login. Django provides pre-written views and URL patterns that handle this with its login system.

Django Caching on front-end

I was working with 2 applications that are within a DJango project: "customer" and "vendors". Each application has a HTML file named "testindex.html".
Whenever I typed:
http://myhost/customer/basic_info
the correct page would show up
If I typed
http://myhost/vendors/basic_info
the page from http://myhost/customer/basic_info would show up
I found out that it was due to caching (since both applications use "testindex.html"). So again, "testindex.html" is caching.
How can one get around this problem?
TIA
Details are listed below. I have the following views defined:
urls.py for the project
urlpatterns = [
... snip ...
url(r'^customer/', include('libmstr.customer.urls')),
url(r'^vendors/', include('libmstr.vendors.urls')),
]
views.py for customer
from django.shortcuts import render
def basic_info(request):
return render(request, 'testindex.html', {})
views.py for vendors
from django.shortcuts import render
def basic_info(request):
return render(request, 'testindex.html', {})
urls.py for customers
from django.conf.urls import url
from . import views
# list of templates
app_name = 'customer'
urlpatterns = [
url(r'^basic_info/$', views.basic_info, name='basic_info'),
]
urls.py for vendors
from django.conf.urls import url
from . import views
# list of templates
app_name = 'vendors'
urlpatterns = [
url(r'^basic_info/$', views.basic_info, name='basic_info'),
]
It sounds like you have two templates, customers/templates/testindex.html and vendors/templates/testindex.html.
When you call render(request, 'testindex.html', {}), the app directories template loader searches the templates directory for each app in INSTALLED_APPS, and stops the first time it finds a match. If customers is above vendors in INSTALLED_APPS, then it will always use the customers template.
For this reason, Django recommends that you name your templates customers/templates/customers/testindex.html and vendors/templates/vendors/testindex.html, and change your views to use customers/testindex.html and vendors/testindex.html. This way you avoid clashes.

How to serve a static file in a Django app only to logged-in users?

I need to serve a particular static asset only to users logged into my Django site. I'm serving static assets through Apache. For various reasons I’m not interested in standing up a full CMS — I just need to make sure non-qualified site visitors cannot download this particular file.
This is really low traffic site so failing all else I can hardcode a urlpattern & serve it as a template.
But there's gotta be a smarter way to do this, right?
EDIT:
Here’s where I settled for now:
# views.py
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
#login_required
def secretfile(request):
return render(request, 'secretfile.xls')
# urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'secretfile.xls', views.secretfile),
]