I'm trying to deploy a website and I need to use different apps. The website is one page that elements change and you can see different pages. But the problem is that because I'm using different apps the Django needs url to call the views.py function and I don't want separated urls for each app. I want to have an template that receives all the views.py variables from different apps and show them. The models are already registered.
Directory:
models.py:
class AboutpageText(models.Model):
aboutText = models.TextField()
class Icons(models.Model):
iconName = models.CharField(max_length=255)
def __str__(self):
return self.iconName
class ServiceItems(models.Model):
serviceIcon = models.CharField(max_length=255)
serviceName = models.CharField(max_length=255)
serviceText = models.TextField()
def __str__(self):
return self.serviceName
views.py:
def aboutpage(request):
aboutpageText = AboutpageText.objects.all()
icons = Icons.objects.all()
serviceItems = ServiceItems.objects.all()
return render(request, "index.html", context={"aboutpageTexts": aboutpageText, "serviceItems": serviceItems, "icons": icons})
aboutpage_app/urls.py:
urlpatterns = [
path('', views.aboutpage)
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
index.html(template):
{% for aboutpageText in aboutpageTexts %}
<strong>{{ aboutpageText.aboutText }}</strong>
{% endfor %}
The values are not passed to the index.html
I tried to have a views file called mainView.py in the main directory(where manage.py is) and pass all values from apps views file to this file then send the variables to template, But the didn't work too.
I believe u can use Ajax for this.
I mean having different URLs with the same path and u can specify a name for them so u can find them, just requesti with ajax
to learn how to use Django with ajax
https://www.tutorialspoint.com/django/django_ajax.htm
https://www.geeksforgeeks.org/handling-ajax-request-in-django/
Related
I'm working on an website where user (if logged in) can perform a calculation and see all of his calculations listed. My website has two groups: Clients and Administrators where Clients are not allowed to see any other members' calculations except their own while Administrators are. Authentication is based on Django's built in User class and for each group I created separate views and templates. Templates have similar behavior but different displaying messages. When Clients want to see their calculations, correct information is displayed but the template rendered is for Administrators rather than for Clients.
views.py
#Administrators' view function
class CalculationListView(generic.ListView):
model = Calculation
paginate_by = 10
#Users' view function
class CalculationsByUserListView(LoginRequiredMixin, generic.ListView):
model = Calculation
paginate_by = 10
def get_queryset(self):
return Calculation.objects.filter(userid=self.request.user)
urls.py
urlpatterns = [
path('', views.index, name='index'),
path('calculations/', views.CalculationListView.as_view(), name='calculations'),
path('calculation/<int:pk>', views.CalculationDetailView.as_view(), name='calculation-detail'),
]
urlpatterns += [
path('mycalculation/', views.CalculationsByUserListView.as_view(), name='mycalcs'),
]
Template names
Administrators: calculation_list.html
Clients: calculations_user.html
EDIT: I mistakenly posted wrong test function. So, here's the one that produced the output referenced few lines below.
test.py
def test_logged_in_uses_correct_template(self):
login = self.client.login(username='testuser1', password='1X<ISRUkw+tuK')
response = self.client.get(reverse('mycalcs'))
# Check if user is logged in
self.assertEqual(str(response.context['user']), 'testuser1')
# Check for the response "success"
self.assertEqual(response.status_code, 200)
# Check if correct template used
self.assertTemplateUsed(response,'cycle/calculations_user.html')
Test response
======================================================================
FAIL: test_logged_in_uses_correct_template (cycle.tests.test_views.CalculationsByUserListViewTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "C:\Users\DEBIEVEL\Desktop\Work\Edited\django_test\orc2\cycle\tests\test_views.py", line 80, in test_logged_in_uses_correct_template
self.assertTemplateUsed(response, 'cycle/calculations_user.html')
File "C:\ProgramData\Anaconda3\lib\site-packages\django\test\testcases.py", line 661, in assertTemplateUsed
% (template_name, ', '.join(template_names))
AssertionError: False is not true : Template 'cycle/calculations_user.html' was not a template used to render the response. Actual template(s) used: cycle/calculation_list.html, base_generic.html
I tried changing mappings and names of the urls but than I get NoReverseMatch error when I run server but the above test passes. I'm not sure where exactly did I mess up so any help is welcome.
You should specify the name of the templates in the ListViews. By default a ListView [Django-doc] will use the name of the model (here calculation), and then add a suffix _list to it, this thus means that a ListView with Calculation as model, will render the calculation_list.html template.
But this makes no sense for the CalculationsByUserListView, you thus should specify the template_name [Django-doc] with:
class CalculationsByUserListView(LoginRequiredMixin, generic.ListView):
model = Calculation
paginate_by = 10
template_name = 'calculations_user.html'
def get_queryset(self):
return Calculation.objects.filter(userid=self.request.user)
So the issue wasn't in the view definition but in the template. Once I applied #Willem Van Onsem's solution, I started getting NoReverseMatch error for part of my template that actually displays the list. Upon further inspection I discovered it is the for loop in the template that actually raised an error. Since I haven't posted template code before, we were not able to spot it earlier.
The old for loop:
<ul>
{% for calc in calculation_list %}
<li> Performed on {{calc.time_calculated}}
</li>
{% endfor %}
</ul>
New for loop:
<ul>
{% for calc in calculation_list %}
<li> Performed on {{calc.time_calculated}}
</li>
{% endfor %}
</ul>
I also needed to move this specific template out of the templates/cycle folder to only templates/
Once, that was done, everything worked fine.
My template structure is base.html where i included navbar.html inside the base
I have an app called tags and tags has a models.py and a views.py
inside the views.py, i have a django code as this
from tags.models import Tag
class TagList(ListView):
model = Tag
def get_queryset(self):
return Tags.object.all()
this works, when i call {{ object_list }} inside my template for the tag_list.html.
So i added the {{ object_list }} inside my template navbar.html which was included inside the base.html but it works only when am on the url that displays my tag_list.html and does not show anything when am on other urls or other templates..
How do I make it show irrespective of the template directory am inside or the url am displaying ... i want it to show
I've thought of copying my views.py code into every app view and repeat the same process for every templates but i have a lot of template directories i cannnot do this for all of them
If you need this variable for all pages you can use a context processor.
Otherwise a mixin class similar to ContextMixin could also do the job:
# views.py
class MyContextMixin:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['tags_list'] = Tags.object.all()
return context
class TagList(MyContextMixin, ListView):
model = Tag
You can then extend each View as follows:
# other views.py
from tags.views import MyContextMixin
class OtherView(MyContextMixin, ...):
...
In both cases you add another context variable. Note that I renamed object_list to tags_list to not override the variable name used by ListView.
There is a common case I encounter, where I can't find a way to split apps.
The case is when a info of two models is related and needs to be in the same template
An example speaks 1000 words: (2 models - pages + comments).
# models.py
class Page(models.Model):
title = models.CharField()
content = models.TextField()
class Comment(models.Model):
page = models.ForeignKey('Page')
content = models.TextField()
# url.py
...
url(r'^page/(?P<page_pk>\d+)/$', views.ViewPage, name='page-view-no-comments'),
url(r'^comment/(?P<comment_pk>\d+)/$', views.ViewComment, name='comment-view'),
url(r'^page-with-comments/(?P<page_pk>\d+)/$', views.ViewPageWithComments, name='page-view-with-comments'),
...
# views.py
def ViewPage(request, page_pk):
page = get_object_or_404(Page, pk=page_pk)
return render(request, 'view_page.html', {'page':page,})
def ViewComment(request, comment_pk):
comment = get_object_or_404(Comment, pk=comment_pk)
return render(request, 'view_comment.html', {'comment':comment})
def ViewPageWithComments(request, page_pk):
page = get_object_or_404(Page, pk=page_pk)
page_comments = Comment.objects.filter(page=page)
return render(request, 'view_page.html', {'page':page,'page_comments':page_comments'})
In this situation, splitting to Page app and Comment app is problematic, because they share a view (ViewPageWithComments) and url.
My options are:
1) Create an Ajax call to comments, which has crawling problems although Google might have fixed it lately.
2) Create a method of page that calls a method in the comments app that returns html with the comments content. If the method needs more arguments I also need to write a custom filter tag.
3) Decide not to split...
Am I missing something and there's another option? When would you prefer (1) vs (2) ?
Note - I created a very simple example to keep the problem general.
You don't need to split anything, you have the pages, and comments have a foreign key to that so you can just iterate over the pages comments
{% for page in pages %}
{% for comment in page.comment_set.all %}
{% endfor}
{% endfor %}
If you want to be able to use the same template for a version of this page without comments you can just wrap the comment for loop in an {% if show_comments %} statement
I have two models : Advertisment and Banner
when I using "generic view" How can I Bring together at the same time
The code below bring only one Advertisment
urlpatterns = patterns('',
url(r'^(?P<pk>\d+)/$', DetailView.as_view(
model = Advertisment,
context_object_name = 'advertisment',
), name='cars-advertisment-detail'),
url(r'^$', SearchView.as_view(), name='cars-advertisment-search'),
)
Aidan's answer is good if you only want to do it for a single view, but if you want to show banners on each page automatically, you have two main options.
One is to create a template tag that renders the banner, and add this tag to your templates where you want banners to be shown.
Your tag could look like this:
#register.inclusion_tag('banner.html')
def banner_display():
random_banner = Banner.objects.order_by('?')[0]
return {'the_banner': random_banner}
Then, you would create a template that shows the banner:
<img src="{{ the_banner.url|safe }}" />
In your templates, where you need the banner, you would just say {% banner_display %}
The other option you have is to create a custom template context processor. This will inject your banner as a normal variable in all requests. This is perhaps even simpler:
def banner_display(request):
random_banner = Banner.objects.order_by('?')[0]
return {'the_banner': random_banner}
You should save this in a file and then add it to your TEMPLATE_CONTEXT_PROCESSORS setting. Now in every template you have a variable {{ the_banner }}.
You need to override the get_context_data() method of the class based view (as described in the docs).
from django.views.generic import DetailView
class YourDetailView(DetailView):
model = Advertisment
context_object_name = 'advertisment'
def get_context_data(self, *args, **kwargs):
context = super(YourDetailView, self).get_context_data(*args, **kwargs)
if 'banner_id' in self.kwargs:
context['banner'] = get_object_or_404(Banner, pk=self.kwargs['banner_id']
return context
I guess you'll need to update your url conf to include a primary key for the Banner model too.
from your_app.views import YourDetailView
url(r'^(?P<ad_pk>\d+)/(?P<banner_pk>\d+)/$', YourDetailView.as_view(), name='cars-advertisment-detail'),
I want to create custom page for admin panel without model. For first i copy index.html to project folder:
mysite/
templates/
admin/
index.html
Then add to apps block my code:
<div class="module">
<table summary="{% blocktrans with name="preferences" %}Models available in the preferences application.{% endblocktrans %}">
<caption>{% blocktrans with name="preferences" %}Preferences{% endblocktrans %}</caption>
<tr>
<th scope="row">Preferences</th>
<td>{% trans 'Change' %}</td>
</tr>
</table>
</div>
This works good, then I create new page /templates/admin/preferences/preferences.html and
add to urls.py:
url(r'^admin/preferences/$', TemplateView.as_view(template_name='admin/preferences/preferences.html')),
And add code to preferences.html:
{% extends "admin/base_site.html" %}
{% block title %}Test page{% endblock %}
Run it and see message with error "The requested admin page does not exist.". What I do wrong?
You need to add your admin URL before the URL patterns of the admin itself:
urlpatterns = patterns('',
url(r'^admin/preferences/$', TemplateView.as_view(template_name='admin/preferences/preferences.html')),
url(r'^admin/', include('django.contrib.admin.urls')),
)
This way the URL won't be processed by Django's admin.
Years go by and still a relevant answer to this can be posted.
Using Django 1.10+ you can do:
security/admin.py (this is your app's admin file)
from django.contrib import admin
from django.conf.urls import url
from django.template.response import TemplateResponse
from security.models import Security
#admin.register(Security)
class SecurityAdmin(admin.ModelAdmin):
def get_urls(self):
# get the default urls
urls = super(SecurityAdmin, self).get_urls()
# define security urls
security_urls = [
url(r'^configuration/$', self.admin_site.admin_view(self.security_configuration))
# Add here more urls if you want following same logic
]
# Make sure here you place your added urls first than the admin default urls
return security_urls + urls
# Your view definition fn
def security_configuration(self, request):
context = dict(
self.admin_site.each_context(request), # Include common variables for rendering the admin template.
something="test",
)
return TemplateResponse(request, "configuration.html", context)
security/templates/configuration.html
{% extends "admin/base_site.html" %}
{% block content %}
...
{% endblock %}
See Official ModelAdmin.get_urls description (make sure you select proper Django version, this code is valid for 1.10 above)
Note the use of get_urls() above.
This new admin page will be
accessible under:
https://localhost:8000/admin/security/configuration/
This page will be protected under admin login area
You should be using admin's get_urls.
If you want to create a custom page just to place there an arbitrary form to handle user input, you may give django-etc a try. There's etc.admin.CustomModelPage you can use:
# admin.py
from etc.admin import CustomModelPage
class MyPage(CustomModelPage):
title = 'My custom page' # set page title
# Define some fields you want to proccess data from.
my_field = models.CharField('some title', max_length=10)
def save(self):
# Here implement data handling.
super().save()
# Register the page within Django admin.
MyPage.register()
Here's an example of everything that should be needed (as of Django 1.6) for a custom admin page that is linked to from a button next to the "History" button in the top right of an object's detail page:
https://gist.github.com/mattlong/4b64212e096766e058b7
Full example:
from django.urls import path
from django.contrib import admin
from django.db import models
class DummyModel(models.Model):
class Meta:
verbose_name = 'Link to my shiny custom view'
app_label = 'users' # or another app to put your custom view
#admin.register(DummyModel)
class DummyModelAdmin(admin.ModelAdmin):
def get_urls(self):
view_name = '{}_{}_changelist'.format(
DummyModel._meta.app_label, DummyModel._meta.model_name)
return [
path('my_view/', MyCustomView.as_view(), name=view_name)
]
With this approach Django's makemigrations command will create DB migration to create table for DummyModel.
Extending the AdminSite class worked best for me, as per django's documentation. This solution protects the page(s) under the admin site login mechanism, and setting it up is easier than it may look:
Where you have other admin code (eg. myapp/admin.py), extend the default class:
from django.contrib.admin import AdminSite
class CustomAdminSite(AdminSite):
def get_urls(self):
custom_urls = [
path('admin/preferences/', self.admin_view(views.my_view)),
]
admin_urls = super().get_urls()
return custom_urls + admin_urls # custom urls must be at the beginning
site = CustomAdminSite()
# you can register your models on this site object as usual, if needed
site.register(Model, ModelAdmin)
Implement the view
def my_view(request):
return render(request, 'admin/preferences/preferences.html')
Use that admin site in urls.py, instead of the default one
from myapp import admin
# now use admin.site as you would use the default django one
urlpatterns = [
# ...
path('admin/', admin.site.urls),
# ...
]
If you want to hook a page into the existing admin site, then you can do the following, which is based on #arnaud-p's answer above. Arnaud's answer didn't work for me, as subclassing adminsite's get_url function lost access to existing admin pages until I added the registry as follows.
Using the following method, your additional pages will require staff access, and you don't need to change your urls.py, so this is great for making admin pages for apps etc... You can pass each_context in the view in order to get permissions etc.
works for django 3.2.9
In admin.py
from django.contrib import admin
from django.urls import path
from . import views
class CustomAdminSite(admin.AdminSite):
def get_urls(self):
self._registry = admin.site._registry
admin_urls = super().get_urls()
custom_urls = [
path('preferences/', views.Preferences.as_view(admin=self), name="preferences"),
]
return custom_urls + admin_urls # custom urls must be at the beginning
def get(self):
request.current_app == self.name
return super().get(request)
def get_app_list(self, request):
app_list = super().get_app_list(request)
app_list += [
{
"name": "My Custom Preferences App",
"app_label": "Preferences",
# "app_url": "/admin/test_view",
"models": [
{
"name": "Preferences",
"object_name": "preferences",
"admin_url": "/admin/preferences",
"view_only": True,
}
],
}
]
return app_list
site = CustomAdminSite()
the view...
class Preferences(views.generic.ListView):
admin = {}
def get(self, request):
ctx = self.admin.each_context(request)
return render(request, 'admin/preferences/preferences.html', ctx)
the template...
{% extends "admin/base_site.html" %}
{% block content %}
...HELLO WORLD!
{% endblock %}