Django namespacing still produces collision - python

I have two apps using same names in my django project. After configuring namespacing, I still get collision. For example when I visit localhost:8000/nt/, I get template from the other app. (localhost:8000/se/ points to the right template).
I must have missed something. Here is the code:
dj_config/urls.py
urlpatterns = [
path("se/", include("simplevent.urls", namespace="se")),
path("nt/", include("nexttrain.urls", namespace="nt")),
# ...
]
dj_apps/simplevent/urls.py
from . import views
app_name = "simplevent"
urlpatterns = [
path(route="", view=views.Landing.as_view(), name="landing")
]
dj_apps/nexttrain/urls.py
from django.urls import path
from . import views
app_name = "nexttrain"
urlpatterns = [
path(route="", view=views.Landing.as_view(), name="landing"),
]
dj_config/settings.py
INSTALLED_APPS = [
"dj_apps.simplevent.apps.SimpleventConfig",
"dj_apps.nexttrain.apps.NexttrainConfig",
# ...
]
TEMPLATES = [
{
# ....
"DIRS": [],
"APP_DIRS": True,
}
Both views will have the same code:
class Landing(TemplateView):
template_name = "landing.html"
Templates are located in:
dj_apps/simplevent/templates/landing.html
dj_apps/nexttrain/templates/landing.html
Note that reversing order of apps in INSTALLED_APPS will reverse the problem (/se will point to nexttrain app).

The usual structure that your template backend expects when looking for template files is: <your app>/templates/<your app>/some_template.html, so your template paths should be:
dj_apps/simplevent/templates/simplevent/landing.html
dj_apps/nexttrain/templates/nexttrain/landing.html
Django does it like this because when collectstatic is run, it practically copies the files from templates folder of each app and places it in one static folder. This is why the app name needs to be twice in the path. Because in the final folder you end up with the app names already there. Also, you could have in your app overridden templates from third party apps (e.g. admin). It sounds a bit too convoluted for me too, but I'm pretty sure that is the intended way you're supposed to separate templates.
Also, in your view, you need to have the app name when specifying the template_name option:
# simplevent app
class Landing(TemplateView):
template_name = "simplevent/landing.html"
# nexttrain app
class Landing(TemplateView):
template_name = "nexttrain/landing.html"
PS. the same principle is applied to static folders.

Related

Django app dissapears from Django Admin site when I add apps.py file

I'm working with Django 3.2 and trying to configure correctly the AppsConfig subclass of apps.py in order to avoid duplicate apps names when initzialing the project.
The context is the following. I have in my INSTALLED_APPS two apps whose names are equal although their paths not:
INSTALLED_APPS = [
...
'first_app.myapp',
'second_app.myapp'
]
To avoid the error shown below (and according to the documentation), I need to create an apps.py file subclassing AppConfig in at least one of the apps called myapp. I've decided to create that file in the second one, second_app.myapp.
django.core.exceptions.ImproperlyConfigured: Application labels aren't unique, duplicates: myapp
The app.py in second_app.myapp module looks like as follows:
from django.apps import AppConfig
class MySecondAppConfig(AppConfig):
name = "second_app.myapp"
label = "my_second_app"
And in the __init__.py I've added:
default_app_config = 'second_app.myapp.apps.MySecondAppConfig'
My admin.py looks like:
from django.contrib import admin
from .models import MyModel
class MySecondAppAdminModel(admin.ModelAdmin):
list_display = ('attr_1', 'attr_2')
admin.site.register(MyModel, MySecondAppAdminModel)
When I start the project all works OK and I can use that model information, views called from second_app.myapp also work OK.
The problem comes when I access to the admin site (http://localhost:8000/admin), where only appears the first_app.myapp admin form instead of both.
Can you help me? Thanks in advance.
instead of this:
INSTALLED_APPS = [
...
'first_app.myapp',
'second_app.myapp'
]
Just try this way only:
INSTALLED_APPS = [
...
'first_app',
'second_app'
]
You got that error because here first_app.myapp and second_app.myapp myapp is duplicate. you should not use like this in both the apps.
OR
INSTALLED_APPS = [
...
'first_app.apps.MyFirstAppConfig',
'second_app.apps.MySecondAppConfig'
]

Using Django all-auth in a standalone app: paths that do not start with 'accounts/' are NOT FOUND 404

I am using Django 3.2, and am creating a standalone user registration and management app, that uses django-allauth to handle all of the registration functionality.
My stand alone app directory structure is like this:
core
/core
settings.py
urls.py
# ...
/django-myown-userprofile
/userprofile
models.py
urls.py
# ...
myapp.tox
setup.py
setup.cfg
# ...
core/core/urls.py
from django.urls import path, include
urlpatterns = [
# ...
path(user, include('userprofile.urls')),
path(admin, admin.site.urls)
]
core/django-myown-userprofile/userprofile/urls.py
from django.urls import path, include
urlpatterns = [
path('accounts', include('allauth.urls')),
path('accounts/profile', ProfileView.as_view(), 'user-profile'),
# ...
]
When I attempt to access any of the allauth endpoints (e.g.):
http://localhost:8000/user/accounts/login
I get the error 404 Not found. It seems that django-allauth is expecting the path to be of the form:
http://localhost:8000/accounts/...
How do I change this behaviour of django-allauth ?
I think you're missing a trailing slash / in your accounts path in userprofile/urls.py:
from django.urls import path, include
urlpatterns = [
path('accounts/', include('allauth.urls')),
path('accounts/profile', ProfileView.as_view(), 'user-profile'),
# ...
]
With that it should work fine. Ensure also that the variables user and admin have string like values that refer to a route ending with a trailing slash. It's nice to be consistent with the trailing slash. You could add it to your ProfileView route to keep the consistency, but it won't matter much.
As an extra thing to keep in mind is that the routes and names you use for your custom views when defining urlpatterns shouldn't overlap the ones used by the accounts app in django-allauth (see https://github.com/pennersr/django-allauth/blob/master/allauth/account/urls.py to check the routes and names used by django-allauth account views). Hope this helped!

Override the automatic generated view_name in Django Rest Framework

I'm creating a Restful API with Django REST Framework. What I have is a project with two different apps. One app is called project/catalog and one is called project/environment. Both of the apps have a model called Device and their own url.py file, which will be included in the project/url.py file.
The urls from the apps are included with a namespace in project/url.py:
# project/url.py
urlpatterns = [
path(r'api/', include(('environment.urls', 'environment'))),
path(r'api/', include(('catalog.urls', 'catalog')))
]
The basename for the urls are automatic generated because in the viewSet I have a attribute queryset
My question is:
Is it possible to override the automatic view_name generation to use the pattern with a namespace? What it says in the documentation is that the view_name is generated like this (model_name)-detail for example. What I want is that the view_name will be generated like this (app_name):(model_name)-detail
I can use the namespace when I create the HyperlinkedIdentityField by my self and add the view_name attribute, like below:
# project/catalog/serializer.py
class DeviceSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='catalog:device-detail')
class Meta:
model = models.Device
fields = [
'url',
...
# project/environment/serializer.py
class DeviceSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='environment:device-detail')
class Meta:
model = models.Device
fields = [
'url',
...
But once I remove the example above it throws the Could not resolve URL for hyperlinked relationship using view name "device-detail" exception. Because the automatic generated view_name excpects device-detail, but I want it to expect catalog:device-detail for the catalog app or environment:device-detail for the environment app.
Some solutions I know about are:
Removing the namespace in project/url.py, but then there are two apps urls which has the same basename. In project/catalog the basename will be device-detail as same for project/environment. This will give issues because it will only look at one basename.
Change the HyperlinkedModelSerializer to just ModelSerializer, but that doesn't really fix my problem for my use case
Define every HyperlinkedIdentityField and add the view_name for every serializer, but that will be a pain for maintenance
My app url files
# project/catalog/url.py
from django.urls import include, path
from rest_framework import routers
from catalog import views
router.register(r'devices', views.DeviceViewSet)
urlpatterns = [
path('catalog/', include(router.urls)),
]
# project/environment/url.py
from django.urls import include, path
from rest_framework import routers
from environment import views
router.register(r'devices', views.DeviceViewSet)
urlpatterns = [
path('environment/', include(router.urls)),
]
EDIT
I have for example the AssemblySerializer in the app environment project/environment/serializer.py. Because I have two apps which can have the same urls I have to use namespaces, which is OK and what I want.
class AssemblySerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = models.Assembly
fields = '__all__'
extra_kwargs = {
'url': {'view_name': 'environment:installedassembly-detail'},
'device': {'view_name': 'environment:device-detail'},
'parent': {'view_name': 'environment:installedassembly-detail'},
'catalog_assembly': {'view_name': 'catalog:assembly-detail'}
}
What I don't want is to set for each serializer the view_name like in the example above. I want that it will automatic look at the namespace of the app environment so that I only have to define a view_name when its outside the app, like the example below.
class AssemblySerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = models.Assembly
fields = '__all__'
extra_kwargs = {
'catalog_assembly': {'view_name': 'catalog:assembly-detail'}
}
You want to use namespaces in this case.
If you are using recent Django then you should specify an app namespace in your urls.py using app_name="catalog". You can override this in include() if needed.
urlpatterns = [
path(r'api/environment/', include('environment.urls')),
path(r'api/catalog/', include('catalog.urls')),
]
Your urls.py can then be simplified like this:
app_name = 'catalog'
urlpatterns = router.urls
And you can reverse the lookup using that namespace (they can also be nested). Read up on setting the view_name on HyperlinkedModelSerializer:
class AccountSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Account
fields = ['account_url', 'account_name', 'users', 'created']
extra_kwargs = {
'url': {'view_name': 'accounts', 'lookup_field': 'account_name'},
'users': {'lookup_field': 'username'}
}
If you need to add custom urls outside the router, then just add them as normal.
urlpatterns += [
path('other/', some_view_func, )
path('ship/', include(("other_thing.urls", "ship"))
]
I highly recommend you install django_extensions installed so you can view your routes:
$> ./manage.py show_urls | column -t
...
PATH VIEW FUNC VIEW NAME
/catalog/items/ app.ItemViewSet catalog:item-list
/catalog/items/<pk>/ app.ItemViewSet catalog:item-detail
...
The include function is a little strange, I'll give you that. I have a helper that I use which makes it consistent, and usually removes the need for routers:
urlpatterns = [
route('layouts/', LayoutViewSet, name="layouts"),
route('admin/', admin_urls, name='admin'),
route('catalog/', 'apps.catalog.urls', name='catalog'),
etc
]

How to point django to put a template in the front page

I am trying to learn python in the django framework, I took a course, but it was based on an earlier version of django while I am using Django 2.1.3 and python 3.7.0, so I've got errors.My question is how can I point urls.py to take an html template and put it in the main page in this version of Django?
Thank you!!
You should point urls.py to views.py for your home page.
So your urlpatterns would look like, your main app:
urlpatterns = [
path(r'', include('page.urls'))
]
urls.py in page app:
urlpatterns = [
url('^$', views.page, name = 'index')]
And views.py like:
def page(request):
return render(request,"page.html")
There is tons of tutorials online. Have a look on Django documentation or YouTube . I also have blog with some info (I started learning recently too) https://adamw.eu
I assume Front page stands for Home page and that your site is called website. Then in urls.py of website/website (the directory that also contains the settings.py file), create a HomePage view
from django.views.generic import TemplateView
class HomePage(TemplateView):
template_name = 'index.html'
Then in urls.pyin the same folder, do something like:
from django.contrib import admin
from django.urls import path, include
from . import views
urlpatterns = [
path('', views.HomePage.as_view(), name="home"),
]
You then need a templates folder at the root of your website, that is in website/templates and create website/templates/index.html which will be your homepage template.
In the website/website/settings.py file, make sure to set TEMPLATE_DIR = os.path.join(BASE_DIR,'templates') and modify the TEMPLATES variable:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [TEMPLATE_DIR],
... # rest of the code here
Reboot server, this should work fine.

Django TemplateDoesNotExist, but traceback reaches views.py

I have a begin.html file that I'm trying to access at localhost:8000/begin, but I get the DjangoTemplateDoesNotExist error when trying to access it.
The only thing in that template is
<h1>welcome to my app !</h1>
and it's in the app/template directory.
In the same app directory is my views.py file, which contains
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.shortcuts import render
# Create your views here.
def begin(request):
print("DEBUG STATEMENT")
return render(request, "template/begin.html", {})
The return statement in begin is reached, as both the print statement and the traceback tell me, but Django can't find the template after.
If it's relevant, my urls.py file is
from django.conf.urls import url
from django.contrib import admin
from app.views import begin
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^begin/$', begin, name = 'begin')
]
in the project directory.
Since you have not provided the information, i take it as you mean urls.py inside your app. If you have two urls.py you need to redirect from the first urls where django looks to the other one.
from django.conf.urls import include
urlpatterns = [
url(r'^', include('YOURAPPNAME.urls'))
]
In your appdirectory in urls.py write
from . import views //importing views from current directory
urlpatterns = [
url(r'^begin/$' views.begin, name=begin)
]
Another tip, consider using render_to_response when you are using a http response, i know render is newer but since you're using a response i would go with render_to_response in your case, and skip the context.
return render_to_response('YOURAPPNAME'/begin.html)
Check if your template structure like this,
your_app_name
--> **templates**
--> **your_app_name**
--> **'begin.html'**
--> static
--> models.py
--> ........
Then, change your views, like this,
return render(request, "your_app_name/begin.html", {})
Django as default check for the directory under the name "templates", in your app_directory.
When accommodating multiple apps, for easiness ​templates are kept under the directory with app_name.
From the docs,
It’s possible – and preferable – to organize templates in subdirectories inside each directory containing templates. The convention is to make a subdirectory for each Django app, with subdirectories within those subdirectories as needed.
Do this for your own sanity. Storing all templates in the root level of a single directory gets messy.
When APP_DIRS is True, DjangoTemplates engines look for templates in the templates subdirectory of installed applications. This generic name was kept for backwards-compatibility.

Categories

Resources