I am using django-registration standard urlpatterns, but i want to rename its template name. By default it is "registration/login.html". In source code there is a parameter "template_name" that i want to change:
class LoginView(RedirectURLMixin, FormView):
"""
Display the login form and handle the login action.
"""
form_class = AuthenticationForm
authentication_form = None
template_name = "registration/login.html"
redirect_authenticated_user = False
extra_context = None
How and where i can do it? (p.s. django.contrib.auth.views.LoginView.template_name = name is not working, or i just write it in incorrect place)
You can change it in urls.py:
urlpatterns = [
...
path('login/', auth.LoginView.as_view(template_name="new_name_login.html"), ...)
]
or you can create your own view (views.py) that is inheriting from the default one:
class MyLoginView(LoginView):
template_name="new_name_login.html"
...
Related
Settings:
LOGIN_URL = 'login_view'
Views:
class MyView(LoginRequiredMixin,View):
template_name = "login.html"
login_url = 'login.html'
redirect_field_name = 'redirect_to'
login_view = MyView.as_view()
Url:
urlpatterns = [
path('admin/', admin.site.urls),
path('',include('blogApp.urls')),
path("login_view/",view=MyView.as_view(template_name = "storefront/login.html"), name="login_view"),
]
And Template is in
./templates/storefront/login.html
I am getting Page not found (404)
You don't Create Get or Post Function in the Class View
views.py
class MyView(LoginRequiredMixin,View):
template_name = "template_name.html"
login_url = lazy_reverse('login_view')
redirect_field_name = 'redirect_to'
def get(self, request):
# do some stuff
def post(self, request):
# do some stuff
in Settings :
You need to put the actual URL or user lazy_reverse(YOUR_URL_NAME)
LOGIN_URL = lazy_reverse('login_view')
this view will work if the user log in else will redirect the User to login_view
in urls:
You write the Template name in the class so You don't need to write it again
path("login_view/",view=MyView.as_view(template_name = "storefront/login.html"), name="login_view"),
here you are writing template_name two times(first in view and second time in url )
if your template is in the following directory structure
templates/storefront/login.html
you only have to describe the template_name in view file and you should also check if
you have registered the template folder in the settings.py file.
I was previously using Django==2.0.6 now I want to upgrade it to Django==2.2.3. And Doing some research I get to know that In django-2.1, the old function-based views of login, logout have been removed.
I still want to add some extra context to LoginView as per my project requirement..
Previously using Function Based View I have done the following:
from django.contrib.auth.views import login as auth_views_login
def login(*args, **kwargs):
"""
Auth Login View
"""
ecom_company = Ecom_Company.objects.filter(pk=1).first()
landing_details = Landing.objects.filter(company=ecom_company).first()
category_list = Categories.objects.filter(
company=ecom_company).exclude(name__exact='Lab Test').order_by('id')
partners_list = Partners.objects.filter(
company=ecom_company).order_by('-id')
stock_list = StockItem.objects.filter(
organisation=ecom_company.organisation).order_by('id')
context = {
'ecom_company': ecom_company,
'landing_details': landing_details,
'category_list': category_list,
'partners_list': partners_list,
'stock_list': stock_list
}
return auth_views_login(*args, extra_context=context, **kwargs)
And in urls:
url(r'login/$', views.login,
{'template_name': 'login.html'}, name="login"),
How do I pass the extra context for Class Based Login View as because Django==2.2 does not support the above.
I think you can use the LoginView like this:
from django.contrib.auth import views as auth_views
class MyLoginView(auth_views.LoginView):
template_name = 'login.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
ecom_company = Ecom_Company.objects.filter(pk=1).first()
landing_details = Landing.objects.filter(company=ecom_company).first()
category_list = Categories.objects.filter(
company=ecom_company).exclude(name__exact='Lab Test').order_by('id')
partners_list = Partners.objects.filter(
company=ecom_company).order_by('-id')
stock_list = StockItem.objects.filter(
organisation=ecom_company.organisation).order_by('id')
context.update({
'ecom_company': ecom_company,
'landing_details': landing_details,
'category_list': category_list,
'partners_list': partners_list,
'stock_list': stock_list
}
return context
# url
url(r'login/$', views.MyLoginView.as_view(), name="login"),
I have a class based view which needs to accept a Form submission. I am trying to auto-populate some of the form fields using the primary key within the URL path (e.g. /main/video/play/135). The class based view is based on FormView, the code I have makes the pk available in context if I use TemplateView, but that is not particularly good for handling forms.
urls.py
app_name = 'main'
urlpatterns = [
#path('', views.index, name='index'),
path('video/<int:pk>', views.VideoDetailView.as_view(), name='detail'),
path('video/preview/<int:pk>', views.VideoPreview.as_view(), name='preview'),
path('player', views.PlayerListView.as_view(), name='player_list'),
path('video/play/<int:pk>/', views.VideoPlayView.as_view(), name='play'),
path('', views.VideoListView.as_view(), name="video_list")
]
Relevant class from views.py:
class VideoPlayView(FormView):
template_name = "main/video_play.html"
form_class = VideoPlayForm
initial = {}
http_method_names = ['get', 'post']
def get_initial(self, **kwargs):
initial = super().get_initial()
#initial['video'] = pk
initial['watch_date'] = datetime.date.today()
return initial
def get_context_data(self, **kwargs):
kc = kwargs.copy()
context = super().get_context_data(**kwargs)
video = Video.objects.get(context['pk'])
context['video'] = video
context['test'] = kc
self.initial['video'] = video.pk
context['viewers'] = Viewer.objects.all()
context['players'] = Player.objects.filter(ready=True)
return context
def form_valid(self, form):
return HttpResponse("Done")
I get a key error at the line:
video = Video.objects.get(context['pk'])
Viewing the debug info on the error page indicates that the context does not have the pk value stored within it.
If I change the base class to TemplateView with a FormMixin I don't get this key error (but I do have problems POSTing the form data), so I know that the code is basically okay. My understanding is that the FormView class should populate context in the same way as the TemplateView class.
Any idea why FormView behaves this way, and how can I get this working?
If you want pk from the URL, self.kwargs['pk'] will work in all Django generic class-based-views.
In TemplateView, the get() method passes kwargs to the get_context_data method, so you can use context['pk']. The FormView get() method calls get_context_data() without passing any kwargs, so that won't work.
I'm using Django 2.0
I have two modesl course and chapter
I want to pass course.pk in CreateView of chapter since chapter is related to course.
This is my urls.py
from django.urls import path
from courses.views import Courses, NewCourse, CourseView, NewChapter
app_name = 'course'
urlpatterns = [
path('all', Courses.as_view(), name='list'),
path('new', NewCourse.as_view(), name='new'),
path('<pk>/detail', CourseView.as_view(), name='detail'),
path('<course_id>/chapter/add', NewChapter.as_view(), name='new_chapter')
]
and NewChapter(CreateView)
class NewChapter(CreateView):
template_name = 'courses/chapter/new_chapter.html'
model = Chapter
fields = ['name']
def get_context_data(self, **kwargs):
context = super(NewChapter, self).get_context_data(**kwargs)
course = Course.objects.get(pk=kwargs['course_id'])
if course is None:
messages.error(self.request, 'Course not found')
return reverse('course:list')
return context
def form_valid(self, form):
form.instance.created_by = self.request.user
form.instance.course = Course.objects.get(pk=self.kwargs['course_id'])
form.save()
I also want to carry on validation if the Course with passed course_id exists or not. If it does not exists user will be redirected back otherwise he will be able to add chapter to it.
But it is giving error as
KeyError at /course/9080565f-76f4-480a-9446-10f88d1bdc8d/chapter/add
'course_id'
How to access parameters of path url in view?
You need to use self.kwargs, not kwargs.
course = Course.objects.get(pk=self.kwargs['course_id'])
My app has users who create pages. In the Page screen of the admin, I'd like to list the User who created the page, and in that list, I'd like the username to have a link that goes to the user page in admin (not the Page).
class PageAdmin(admin.ModelAdmin):
list_display = ('name', 'user', )
list_display_links = ('name','user',)
admin.site.register(Page, PageAdmin)
I was hoping that by making it a link in the list_display it would default to link to the actual user object, but it still goes to Page.
I'm sure I'm missing something simple here.
Modifying your model isn't necessary, and it's actually a bad practice (adding admin-specific view-logic into your models? Yuck!) It may not even be possible in some scenarios.
Luckily, it can all be achieved from the ModelAdmin class:
from django.urls import reverse
from django.utils.safestring import mark_safe
class PageAdmin(admin.ModelAdmin):
# Add it to the list view:
list_display = ('name', 'user_link', )
# Add it to the details view:
readonly_fields = ('user_link',)
def user_link(self, obj):
return mark_safe('{}'.format(
reverse("admin:auth_user_change", args=(obj.user.pk,)),
obj.user.email
))
user_link.short_description = 'user'
admin.site.register(Page, PageAdmin)
Edit 2016-01-17:
Updated answer to use make_safe, since allow_tags is now deprecated.
Edit 2019-06-14:
Updated answer to use django.urls, since as of Django 1.10 django.core.urls has been deprecated.
Add this to your model:
def user_link(self):
return '%s' % (reverse("admin:auth_user_change", args=(self.user.id,)) , escape(self.user))
user_link.allow_tags = True
user_link.short_description = "User"
You might also need to add the following to the top of models.py:
from django.template.defaultfilters import escape
from django.core.urls import reverse
In admin.py, in list_display, add user_link:
list_display = ('name', 'user_link', )
No need for list_display_links.
You need to use format_html for modern versions of django
#admin.register(models.Foo)
class FooAdmin(admin.ModelAdmin):
list_display = ('ts', 'bar_link',)
def bar_link(self, item):
from django.shortcuts import resolve_url
from django.contrib.admin.templatetags.admin_urls import admin_urlname
url = resolve_url(admin_urlname(models.Bar._meta, 'change'), item.bar.id)
return format_html(
'{name}'.format(url=url, name=str(item.bar))
)
I ended up with a simple helper:
from django.shortcuts import resolve_url
from django.utils.safestring import SafeText
from django.contrib.admin.templatetags.admin_urls import admin_urlname
from django.utils.html import format_html
def model_admin_url(obj: Model, name: str = None) -> str:
url = resolve_url(admin_urlname(obj._meta, SafeText("change")), obj.pk)
return format_html('{}', url, name or str(obj))
Then you can use the helper in your model-admin:
class MyAdmin(admin.ModelAdmin):
readonly_field = ["my_link"]
def my_link(self, obj):
return model_admin_url(obj.my_foreign_key)
I needed this for a lot of my admin pages, so I created a mixin for it that handles different use cases:
pip install django-admin-relation-links
Then:
from django.contrib import admin
from django_admin_relation_links import AdminChangeLinksMixin
#admin.register(Group)
class MyModelAdmin(AdminChangeLinksMixin, admin.ModelAdmin):
# ...
change_links = ['field_name']
See the GitHub page for more info. Try it out and let me know how it works out!
https://github.com/gitaarik/django-admin-relation-links
I decided to make a simple admin mixin that looks like this (see docstring for usage):
from django.contrib.contenttypes.models import ContentType
from django.utils.html import format_html
from rest_framework.reverse import reverse
class RelatedObjectLinkMixin(object):
"""
Generate links to related links. Add this mixin to a Django admin model. Add a 'link_fields' attribute to the admin
containing a list of related model fields and then add the attribute name with a '_link' suffix to the
list_display attribute. For Example a Student model with a 'teacher' attribute would have an Admin class like this:
class StudentAdmin(RelatedObjectLinkMixin, ...):
link_fields = ['teacher']
list_display = [
...
'teacher_link'
...
]
"""
link_fields = []
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.link_fields:
for field_name in self.link_fields:
func_name = field_name + '_link'
setattr(self, func_name, self._generate_link_func(field_name))
def _generate_link_func(self, field_name):
def _func(obj, *args, **kwargs):
related_obj = getattr(obj, field_name)
if related_obj:
content_type = ContentType.objects.get_for_model(related_obj.__class__)
url_name = 'admin:%s_%s_change' % (content_type.app_label, content_type.model)
url = reverse(url_name, args=[related_obj.pk])
return format_html('{}', url, str(related_obj))
else:
return None
return _func
If anyone is trying to do this with inline admin, consider a property called show_change_link since Django 1.8.
Your code could then look like this:
class QuestionInline(admin.TabularInline):
model = Question
extra = 1
show_change_link = True
class TestAdmin(admin.ModelAdmin):
inlines = (QuestionInline,)
admin.site.register(Test, TestAdmin)
This will add a change/update link for each foreign key relationship in the admin's inline section.