Following from my question Django sharing one model between two others which explored the best way to generically manage two similar models and their dependencies. I've now come to the next part - the views.
After a bit of tweaking around I've come to the following generic listview to represent the objects.
Is this coherent? Should the urls manage the models like this?
I believe if I refine their logic I can just subclass this itemlist and add additional logic in the future so it should be future-proofed.
A class that
urls.py excerpt
urlpatterns = [
url(r'^item/(?P<model>\w+)/$',views.ItemList.as_view(),name='generic'),
url(r'^item/(?P<model>\w+)/(?P<pk>\d+)$',views.ItemList.as_view(),name='generic'),
]
views.py excerpt
class ItemList(ListView):
context_object_name = 'itemlist'
template_name = 'food/item.html'
model = None
def get_queryset(self):
model = apps.get_model('food',self.kwargs.get('model'))
if self.kwargs.get('pk'):
print ('pk provided')
return model.objects.filter(pk=self.kwargs.get('pk'))
else:
print ('all')
return model.objects.all()
food/item.html excerpt (using registered filter to get class name for object)
{% block content %}
{% for item in itemlist %}
...
{% for review in item.reviews.all %}
{% endfor %}
...
<span class="glyphicon glyphicon-plus"> Add Review </span>
{% endblock %}
Also - can I carry this attitude into my forms (again assuming that when I add a review it'll generically link back to whatever object it came from?) or is that where I should start breaking them up?
Related
new try of dev here.
Im trying to make a project and I alredy having some issues that i dont know why dont work... I put my code and explain it...
url.py
app_name = 'opotest'
urlpatterns = [
url(r'^$', views.indexView, name='index'),
url(r'^inicio/$', views.ListaView.as_view(), name='inicio'),
url(r'^test/(?P<tipo>.+)/$', views.TestList.as_view(), name='test'),
url(r'^test/(?P<tipo>.+)/run/$',
views.TestDetail.as_view(), name='run'), # this one
View.py
class TestDetail(generic.DetailView):
model = Pregunta
context_object_name = 'lista'
def get_queryset(self):
return Pregunta.objects.all()
HTML template
{% for test in lista %}
<br/>
<p>Pregunta: {{ pregunta.textopregunta }}</p>
{% endfor %}
Models.py
class Pregunta(models.Model):
id = models.AutoField(primary_key=True)
textopregunta = models.CharField('Texto pregunta', max_length=1000)
test = models.ForeignKey(Test, on_delete=models.CASCADE)
def __str__(self):
return self.textopregunta
This code should bring me some 'pregunta' that i have alredy created but it doesnt work... can you please tell me what im doing wrong? the for bring me Pregunta: the textopregunta do not appears...
Thanks everyone
D...
Should be
{% for test in lista %}
<br/>
<p>Pregunta: {{ test.textopregunta }}</p>
{% endfor %}
will work,
but anyway, if you want to use Pregunta.objects.all() , you must use ListView, not DetailView. Like this
class TestList(generic.ListView):
model = Pregunta
context_object_name = 'lista'
{% for test in lista %}
<br/>
<p>Pregunta: {{ test.textopregunta }}</p>
{% endfor %}
UPDATE
If you want to list all your model objects, please use ListView.
1. Change your view to ListView
class TestList(generic.ListView):
model = Pregunta
context_object_name = 'lista'
Then you can use objects and lista both in tempalte.
2. Change your url
You don't need tipo in url, if it's listview.
urlpatterns = [
url(r'^$', views.indexView, name='index'),
url(r'^inicio/$', views.ListaView.as_view(), name='inicio'),
url(r'^test/(?P<tipo>.+)/$', views.TestList.as_view(), name='test'),
url(r'^test/run/$',
views.TestList.as_view(), name='run'), # this one
3. use objects or lista in templates.
Because you define lista in context_object_name : it means your objects list will be used with lista in template. (you can use objects cause django ListView automatically make context for you)
{% for test in lista %}
<br/>
<p>Pregunta: {{ test.textopregunta }}</p>
{% endfor %}
UPDATE for DetailView
1. Change urls with pk
Above all, you have to understand about View - Template and urls.
In your urls - you should pass params what you want to use to grep your one specific object. It should be unique, so just use pk(id). (Or you can make your own unique slug)
url(r'^test/run/$',
views.TestList.as_view(), name='run'), # this one
# this is DetailView
url(r'^test/(?P<pk>\d+)/$',
views.TestDetail.as_view(), name='run'), # this one
Then your url will be.. test/1/ , test/2/.
2. change views
Actually, django CBV supports many functions (method) so you don't have to handle more but it's hard to understand.
class TestDetail(generic.DetailView):
model = Pregunta
context_object_name = 'lista'
You're confusing when you don't know about DetailView.
In DetailView, (as you can see here-ccbv.co.kr ) it has get_object() method. If any url_kwargs defined in your view, it use pk for basic. So it will find like Pregunta.objects.get(id=self.kwargs.get(pk)) that takes from your urls.py.
Then you can use your object in template, using lista or object.
<br/>
<p>Pregunta: {{ lista.textopregunta }}</p>
You DONT have to forloop all objects (actually CANT) because DetailView basically find your object from your kwargs (in this situation, pk).
I highly recommend reading django docs (CBV) and seeing ccbv.co.kr for understanding CBV, or trying to use FBV in django.
try to override the get_context_data() method, as below then change your template also,
class TestDetail(generic.DetailView):
model = Pregunta
context_object_name = 'lista'
def get_queryset(self):
return Pregunta.objects.all()
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['sample'] = self.get_queryset()
return context
Try to change your template as below,
{% for test in sample %}
<br/>
<p>Pregunta: {{ test.textopregunta }}</p>
{% endfor %}
By default Generic.DetailView looks for pk in the url parameters which is used to search data from your database. In your case you are using tipo as url parameters. So, you need to override the default inside your view. Your code should be something like this:
class TestDetail(generic.DetailView):
model = Pregunta
context_object_name = 'lista'
pk_url_kwarg = 'tipo'
def get_queryset(self):
return Pregunta.objects.all()
Now it will
look for tipo in your url and take the parameters to search from your database. More on Generic views can be found on this link: https://ccbv.co.uk/projects/Django/1.10/django.views.generic.detail/DetailView/
I'm new to Django and programming. I'm trying to call the field from a model through a template other than specified in my url patterns but I keep on getting a blank display in the div block I want i to display in. I want a list of categories to be displayed in the left side-bar. I have looked at many answers on SO Django foreign key relation in template and How to display one field from a model in Django template and Ive worked through the Django docs.
I've created a list of categories in the admin class. The code is working as intended but Im not able to extend this list to be displayed from the base.html template class so that I have category navigation throughout.
I have two models:
class Post(models.Model):
category = models.ForeignKey(Categories, related_name='topic')
...
class Categories(models.Model):
topics = models.CharField(max_length=100)
...
Here is my view which works in the 'categories.html' template
def categories(request):
category = Categories.objects.all()
return render(request, 'posts/categories.html', {"category": category})
The url pattern related to it
url(r'^categories/$', views.categories, name='category'),
Here is how i'm calling the field
{% for field in category %}
<div class="col-sm-10">{{ field.topics }}</div>
{% endfor %}
I've tried alot of different approaches such as:
{% for field in category.topic.all %}
{% endfor %}
Please assist. Thank you in advance.
Given this apphook:
class NewsHook(CMSApp):
name = _("News")
urls = ["apps.news.urls"]
apphook_pool.register(NewsHook)
and this model inside apps.news.models:
class Article(models.Model):
title = models.CharField(max_length=255)
...
Is it possible to reference the page associated by apphook in, say, a method on Article?
From the model side I've gotten as far as article._meta.app_label or article._meta.app_config.verbose_name, but that only yields 'news' and 'News', respectively.
And I know from https://github.com/divio/django-cms/blob/7888ab8421bb836c8f7a1127d9a2bf4d4bbdf23e/cms/models/pagemodel.py#L82 that a page's apphook is accessible with page.application_urls, which gives me 'u'NewsHook'.
But I'm missing a link.
I suppose I could filter Pages by the application_urls field and look for a match with my article._meta.app_config.verbose_name, but that would be neither neither failsafe nor pretty.
Any ideas for a better way?
I know this question is a year old and probably OP have figured it out, but I had a similar problem, which I solved by referencing the apphook directly on the method.
from applications.cms_apps import ApplicationAppHook
from cms.models.pagemodel import Page
class Application(models.Model):
def related_cms_page(self):
return Page.objects.filter(application_namespace=ApplicationAppHook.app_name).public().first()
I've gone somewhat further and created a templatetag that uses the application_namespace value to retrieve the page
from cms.models.pagemodel import Page
#register.assignment_tag()
def get_page_by_namespace(application_namespace_str):
try:
return Page.objects.filter(application_namespace=application_namespace_str).public().first()
except AttributeError:
# EAFP ;)
return None
And on the template:
{% get_page_by_namespace 'applications_apphook' as page %}
{% if page %}
{{ page.get_menu_title }}
{# Official Django CMS templatetags also works in this instance, i.e. {% page_attribute "page_title" page %} but it seems a bit redundant to me #}
{% endif %}
So I'd like to think what I'm doing is fairly common. I'm attempting to display a result set using Django. I have looked online for a while and here is what I've come up with:
models.py{
from django.db import models
from django.conf import settings
class SalesRep(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL)
class Sales(models.Model):
seller = models.ForeignKey(SalesRep)
sold_on = models.DateField("Sold On")
}
sales_view.py{
from salesteam.models import SalesRep, Sales
from django.views.generic import ListView
class SellerHome(List View):
model = SalesRep
template_name = 'production/sales.html'
#property
def rep_sales(self):
return Sales.objects.filter(SalesRep=self)
}
sales.html{
<div id="total-sales">
{% for sale in SellerHome.rep_sales%}
<li>{{ sale.id }}</li>
{% empty %}
<li>You don't currently have any sales</li>
{% endfor %}
</div>
}
For the sake of completeness I even tried writing just the property out as Sales.objects.all() but still no luck. Thanks for taking a look as I'm fairly new to HTML, Django, and Python. Any help on what I'm doing wrong would be much appreciated. Let me know if you have any questions :)
I'm not sure what tutorial you've followed, but there is a lot of errors in your code... I'm assuming you would like to display all Sales for the logged in user?
In this scenario, I would change the model slightly to add a related_name to Sales foreign key to SalesRep:
class SalesRep(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL)
class Sales(models.Model):
seller = models.ForeignKey(SalesRep, related_name='sales')
sold_on = models.DateField('Sold On')
Documentation: https://docs.djangoproject.com/en/1.7/ref/models/fields/#django.db.models.ForeignKey.related_name
And I would change the views to use DetailView:
from django.views.generic import DetailView
class SellerDetailView(DetailView):
model = SalesRep
template_name = 'production/sales.html'
context_object_name = 'seller'
def get_object(self):
return self.request.user
Docs on DetailView — https://docs.djangoproject.com/en/1.7/ref/class-based-views/generic-display/#detailview
<div id="total-sales">
<ul>
{% for sale in seller.sales.all %}
<li>{{ sale.id }}</li>
{% empty %}
<li>You don't currently have any sales</li>
{% endfor %}
</ul>
</div>
First, I'd recommend avoiding class based views, at least as you are learning Django, since they are not really necessary and needlessly complicated for what you are doing.
Anyway, to answer your question using class-based views, your problem is in your template and your view. You'll want to put your rep_sales logic in a get_queryset, set the model to be Sales, not SalesRep, and then reference object_list in the template. More info.
However, I'd instead recommend writing a normal, function-based Django view. Something like this might get you started:
def view_all_sales(request):
my_sales_rep = SalesRep.objects.get(request.user)
all_my_sales = Sales.objects.filter(seller=my_sales_rep)
return render(request, "production/sales.html", {
"sales": all_my_sales
})
I have a Survey and a Choice model, and each survey has a number of choices associated with it. When I'm rendering the actual HTML survey page with all the choices, I use the following Django templating code:
{% for choice in survey.choice_set.all %}
<li class="ui-state-default" choice_id={{ choice.id }}>{{ choice.choice_text }}</li>
{% endfor %}
However, instead of the choices appearing in the same order every time, I'd like them to populate in a random order to reduce any potential bias effect (e.g. someone may be more likely to vote for options that appear first on the list).
If there's a way to do this within the template itself, that'd be great, but it seems more likely that I'd need to do something in the backend in views.py. I've already tried this, to no effect:
class DetailView(generic.DetailView):
model = Survey
...
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
...
survey = get_object_or_404(Survey, survey_link__iexact=survey_link)
...
if randomize_choice_order:
survey.choice_set.order_by('?')
...
return context
Any idea how I can accomplish this? Maybe I need to instead develop a JS function to randomize the objects after they've already been placed?
You can create a custom template tag to shuffle your results.
# app/templatetags/shuffle.py
import random
from django import template
register = template.Library()
#register.filter
def shuffle(arg):
aux = list(arg)[:]
random.shuffle(aux)
return aux
Then in your template
{% load shuffle %}
{% for choice in survey.choice_set.all|shuffle %}
With Django 1.7+ you can use the Prefetch object:
survey = get_object_or_404(
Survey.objects.prefetch_related(
Prefetch('choice', queryset=Choice.objects.order_by('?'),
to_attr='random_choices')
),
survey_link__iexact=survey_link
)
You can then access the random set with survey.random_choices. The original choice set is still available with survey.choice_set.all().