Please note: Just to make clear '[app_name]' is a placeholder for the actual app name. Not Django substitution code. Just imagine it says 'Stuff' instead of [app_name], if it's confusing.
My question:
How do I make this more DRY?
There is a lot of code repetition, and there must be a way of unifying some of it. If you do answer, I would be really grateful if you write out explicitly what and why. As a lot of answers assume quite a bit of knowledge and I am trying into good habits in Django coding style and practice. Thank you for your time.
[app_name]/urls.py
from django.conf.urls import url
from . import views
app_name = 'things'
urlpatterns = [
url(r'^cars/$', views.CarThingIndexView.as_view(), name='Car_index'),
url(r'^trees/$', views.TreeThingIndexView.as_view(), name='Tree_index'),
....
]
[app_name]/model.py
from django.db import models
class Tree(models.Model):
"""
Tree
"""
name_text = models.CharField(max_length=200)
def __str__(self):
return self.name_text
class Car(models.Model):
"""
Car
"""
name_text = models.CharField(max_length=200)
def __str__(self):
return self.name_text
[app_name]/view.py
from django.views import generic
from inventory.models import Car, Tree
class CarThingIndexView(generic.ListView):
template_name = '[app_name]/index.html'
context_object_name = 'thing_list'
def get_queryset(self):
return Car.objects.values()
class TreeThingIndexView(generic.ListView):
template_name = '[app_name]/index.html'
context_object_name = 'thing_list'
def get_queryset(self):
return Tree.objects.values()
[app_name]/template/[app_name]/index.html
{% extends '[app_name]/base.html' %}
{% block content %}
{% if thing_list %}
<ul>
{% for item in thing_list %}
<li>
<p>
{{ item }}
</p>
</li>
{% endfor %}
</ul>
{% else %}
<!-- I am pretty sure if there are no objects this will not work, please correct me if I am wrong {{ obj | get_class_name }}. I would like it to read "No Tree/Car are available." -->
<p>No [class_name] are available.</p>
{% endif %}
{% endblock content %}
From my point of view everything is fine and there is no need abstract more in this case. At this point it looks like you can optimize it - but what you've scaffolded does not include any logic. Behind the scenes django already helps you a lot to not repeat yourself here!
When you now start implementing features for your cars and trees the views, models and templates will go in different directions. Next to the name they will have different properties. They act different and were displayed different. This is what usually happens in your application.
If you have a set of models that share a lot of properties and the representation django provides mechanisms like abstract base classes for models properties and methods or the include and extends template tags, if you need to share representations. Regarding view logic that should be shared you might write your own modules or even packages to do whatever you need to do and use them inside your views.
Without real use cases in mind you should not think about how to not repeat yourself at this point.
Related
Am having a problem. And l request for your Help.
Am having 3 apps in Django project
action
adventure
others
`#action/ models.py
....
class Action(models.Model):
name=models.Charfield()
os= models.Charfield( choices=OS)....
#adventure/models.py
....
class Adventure(models.Model):
name=models.Charfield()
os= models.Charfield( choices=OS)....
#Others/views.py
from itertools import chain
from action.models import Action
from adventure.models import Adventure
def windows_games(request):
win_action = Action.objects.filter(os='windows')
win_adventure = Adventure.objects.filter(os='windows')
combined_list = list(chain(win_action,win_adventure))
context = ['combined_list':combined_list,]
return render(request, 'others/os/windows_game.html' , context)
#others/os/windows_game.html
.....
<div class="container">
<img src="{{combined_list.game_pic}}">
<p>{{combined_list.name}}</p>
1). I need to correct me in #others/ views.py if there is any mistake done.
2). I would like to know how to know how to write the tag that outputs the results in #others/os/windows_game.html because I tried that but outputs nothing.
And l would like to be in form of, #{{combined_list.game_pic}}, etc
It needed to display all items created in those models to display in windows_game page, after filtering those that only follow in category of windows.
I tried creating the context but there was no output results.
One of my friend here helped me like that bellow and it worked for me
#Windows_game.html
{% for game in combined_list %}
<p> {{game.game_name}}</p>
......
{% endfor %}
I want to include some basic statistics about a model in a stats.html file. The variables don't show in the html. What am I doing wrong?
from django.shortcuts import render, get_object_or_404, redirect
from django.db.models import Avg, Sum, Count
from .models import Production
def statistics(request):
nr_of_plays = Production.objects.count()
nr_of_actors = Production.objects.aggregate(num_actors=Sum('nr_actors'))
nr_of_audience = Production.objects.aggregate(num_audience=Sum('est_audience'))
context = {
'nr_of_plays': nr_of_plays,
'nr_of_actors': nr_of_actors['num_actors'],
'nr_of_audience': nr_of_audience['num_audience'],
'test':'abc'
}
return render(request, 'stats.html', context)
The model:
class Production(models.Model):
title = models.CharField(max_length=200)
nr_actors = models.IntegerField(default=0)
est_audience = models.IntegerField(default=0)
...
urls.py:
path('stats/', views.statistics, name='stats'),
the relevant section of base.html:
<copyright class="text-muted">
<div class="container text-center">
<p>© One World Theatre - {% now "Y" %} {% include 'stats.html' with test=test %} </p>
</div>
</copyright>
And the stats.html template:
{% load static %}
{{ test }} - Stats: {{ nr_of_plays }} plays produced, involving {{ nr_of_actors }} actors, seen by {{ nr_of_audience }} people.
the output:
© One World Theatre - 2020 - Stats: plays produced, involving actors, seen by people.
EDIT:
I didn't mention that I'm using my template stats.html in my base.html template like this {% include 'stats.html' %}. When I add with test=test to the include tag, the test text shows. But when adding with nr_of_plays=nr_of_plays nothing happens :-/.
I ended up forgetting about trying to {% include 'stats.html' %} in my base template and just added those variables where I need them, works great. Not DRY, but what to do... .
EDIT 2:
I was too quick to cry victory. Edited the question with the latest code. Passing the variables in the view that handles the main content block works, but that means I would have to add them in every single view (not DRY). Still not getting what doesn't work with my setup. example.com/stats.html renders exactly what I want, but doesn't show the variables when I include it in my base.html. with test=test doesn't do anything. Clueless (and thankful for the help sofar).
Aggregate returns a dictionary.
You need to access its value via the key
context = {
'nr_of_plays': nr_of_plays,
'nr_of_actors': nr_of_actors['nr_actors_sum'],
'nr_of_audience': nr_of_audience['est_audience_sum']
}
Alternatively you can specify a custom key name instead of the default composite one:
nr_of_actors = Production.objects.aggregate(num_actors=Sum('nr_actors'))
nr_of_audience = Production.objects.aggregate(num_audience=Sum('est_audience'))
Note: .all() is redundant and can be removed
Base on your latest confession and symptoms, you don't seem to be going to your statistics view.
Looks like the url is rendering another view, which also extends base.html confuses you that you are in the right view.
One way to test it is to put a print statement in your statistics view and see if it prints anything in the console:
def statistics(request):
print(111111111111111111111111111111)
...
return render(request, 'stats.html', context)
Second thing is, if your base.html includes stats.html, you shouldn't be rendering the stats.html directly, you should pass the context to a template that extends base.html.
Third thing is, refer to Pynchia's answer to properly get the count of aggregated queryset.
I'm setting up a webstore manager and I'm relying on django-tables2 package to show a list of products using the SimpleTableMixin. I want to add the filter/search capability to the view. As recommended by the django-tables2 package one can rely on django-filter package to provide filtering. However, it the case of a model with many fields it becomes almost impossible to query and develop forms for those efficiently. My goal is to use django-haystack to have a single input search form as a mean to query the model instances which should be displayed in a similar table/form.
I've tried to add the SimpleTableMixin to the django-haystack package generic SearchView. However I keep on getting the following error:
TypeError at /manager/front/products/
Argument data to ProductTable is required
So far my implementation goes as follow:
view:
# ############## Products ############## (previous implementation with django-filter)
# #method_decorator(staff_member_required, name='dispatch')
# class ProductList(SingleTableMixin, FilterView):
# model = Product
# table_class = tables.ProductTable
# filterset_class = filters.ProductFilter
# template_name = 'manager/front/products/product_list.html'
############## Products ##############
#method_decorator(staff_member_required, name='dispatch')
class ProductList(SingleTableMixin, SearchView):
model = Product
table_class = tables.ProductTable
template_name = 'manager/front/products/product_list.html'
tables:
import django_tables2 as tables
from front.models import Product
class ProductTable(tables.Table):
class Meta:
model = Product
template_name = 'django_tables2/bootstrap.html'
search_indexes.py:
from haystack import indexes
from front.models import Product
class ProductIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
def get_model(self):
return Product
urls:
path('front/products',views.ProductList.as_view(),name="manage_products"),
template:
{% extends 'base_site.html' %}
{% load render_table from django_tables2 %}
{% block content_title %}Manage Products{% endblock%}
{% block content %}
<form action="" method="get" class="form form-inline">
{{ form }}
<button class="btn btn-default" type="submit">Search</button>
</form>
{% render_table table %}
{% endblock %}
How can I remove that error and provide efficient search abilities to my list views?
Are you sure that you are using haystack.generic_views.SearchView and not haystack.views.SearchView? Please notice that on https://django-haystack.readthedocs.io/en/latest/views_and_forms.html it says:
As of version 2.4 the views in haystack.views.SearchView are deprecated in favor of the new generic views in haystack.generic_views.SearchView which use the standard Django class-based views which are available in every version of Django which is supported by Haystack.
Thus if you're using haystack.views.SearchView then the get_context_data of SingleTableMixin will never be called thus no table will be put in your context (i.e table will be empty). Actually because I dislike the behavior of {% render_table %} when its parameter is empty (it behaves different than the other Django tags/filters i.e it throws an exception while the django ones silently ignore that) I usually put it inside some {% if table %} checks.
UPDATE
It seems that for whatever reason the data is not passed to the table. I am not sure why, I can't test it right now but from a quick look at the source code your implementation should have been working (considering that SearchView has a get_queryset and TableMixin uses the get_queryset to retrieve its data). In any case you try overriding some methods of TableMixin to make sure that the table is properly returned (take a look at TableMixin here: https://django-tables2.readthedocs.io/en/latest/_modules/django_tables2/views.html).
I think the most definite solution would be to just bite the bullet and override get_table yourself. So, try adding something like this to your class:
def get_table(self, **kwargs):
table_class = self.get_table_class()
# I only change this line from the original to make sure that the self.get_queryset() is called to return the data
table = table_class(data=self.get_queryset(), **kwargs)
return RequestConfig(self.request, paginate=self.get_table_pagination(table)).configure(
table
)
One idea that just popped in my mind. Is there a possibility that the get_queryset() method of SearchView returns None due to bad configuration or whatever?
I have a few components on my website that appear on many pages. DRY in mind, I would like to factor them out in separate snippets that I can include in the views that need them.
If it were only static items, then an {% include "snippet.html" %} would be the perfect solution. How can achieve a similar thing for snippets that include forms (and hence logic in the view) or require calculations before being displayed? Also, I would like to be able to nest the snippets several levels deep.
I know I can put simple logic in the template, using {% if ... %} ... {% endif %} blocks, but this turns into horrible spagetthi very soon and I want to keep the business logic separated from the presentation logic.
I am imagining a pattern as follows (here with oversimplified business logic):
def view1(request):
"Display some data"
total = get_total_vote_count()
return render(request, 'snippet1.html', {'total': total})
def view2(request, pk):
"Display some data about the article with primary key pk."
votes = get_votes_for_article(pk)
render1 = view1(request)
return render(request, 'snippet2.html', {'votes': votes, 'render1': render1})
def view3(request, pk):
"Display article pk and some additional data from view1 and view2":
article = get_object_or_404(Article, pk=pk)
render2 = view2(request, pk)
return render(request,
'article.html',
{'article': article, 'render2': render2},
)
with the templates something like:
# in snippet1.html:
{{ total }}
# in snippet2.html:
<p>Votes for this article: {{ votes }} out of {{ render1 }} total votes.</p>
# in page.html:
{% extends "base.html" %}
{% block "content" %}
<h1>article.title</h1>
<p>article.text</p>
<small>{{ render2 }}</small>
{% end block "content" %}
Note that there will be more views that will use view1 and view2 (e.g. an overview of the votes for all articles); that is why I have factored them out in separate functions.
How can I make this work?
Or is there a better trick in the Django toolbox to make this work without repeating view1 and view2 every time I want to use the same snippets in other views?
This is what custom template tags - specifically, inclusion tags - are for: rendering a template fragment with its own context.
what about django middleware, you can use middleware for this case. view1 and view2 repeating every time right ?. then attach the render1 and render2 to your request.
Middleware is a framework of hooks into Django’s request/response processing. It’s a light, low-level “plugin” system for globally altering Django’s input or output.
https://docs.djangoproject.com/en/dev/topics/http/middleware/
I am using mongoengine with Django. I have a class, Noun, from which several other classes (Person, Place, Event, etc.) inherit. The Noun class looks like this:
class Noun(Document):
label = StringField(max_length=120, required=True)
contributor = StringField(max_length=120, required=True)
description = StringField(required=False)
#property
def noun_cls():
return self._cls
meta = {'allow_inheritance': True}
When I try to reference the noun_cls property in the template, I get nothing. For example:
{% for noun in Nouns %}
<li>
Edit {{ noun.noun_cls }}
<p>{{ noun.description }}</p>
</li>
{% endfor %}
...this results in a url like "...noun/update/[long mongo id]//". It is as if the noun_cls property is being totally ignored. This is true both with previously existing nouns (or whatever type), and with new ones made after this code change. Any ideas?
change def noun_cls() to def noun_cls(self) would work!
If you are using Django template engine, it will ignore "non-exist" properties.
In your case, your def noun_cls() is missing self, django gives nothing instead of raising an exception.