In views I want to get car-object, then change first_year var.
def cars(request, mark_id, year=year):
#BMW, etc.
mark = get_object_or_404(Mark, pk=mark_id, active=1)
#M, X-series, etc.
for group in mark.groups.iterator():
group.first_year.year = int(year)-int(group.first_year.year)
return render(request, 'cars.html', { 'mark':mark, })
So, in template I use:
{% for i in mark.groups.all %}
{{i.first_year}}
{% endfor %}
And it returns the values from db, not my group.first_year.year. As you can understand, there is 3 models - mark, groups and first_year. If you need, I can publish they here, but I think, that something wrong in my views.
Thanks.
Edit. Models.
class First_Year(models.Model):
year = models.IntegerField()
def __unicode__(self):
return str(self.year)
class Groups(models.Model):
many_other_fields
mark = models.ForeignKey(Mark, related_name='groups')
last_update = models.DateTimeField()
first_year = models.ForeignKey(First_Year, related_name='first_year')
def __unicode__(self):
return self.name
def altered_date(self, year):
altered_year = int(year)-int(self.first_year.year)
return altered_year
Without model Mark, bacause it has many-many fields without year and etc.
The for loop making changes in your view is not saving the changes anywhere so you will not see the changes when the values are passed to your template.
One solution here is to add a new model method to your model and do the date comparison there.
In your First_Year model add the altered_date function like so:
class First_Year(models.Model):
year = models.IntegerField()
def __unicode__(self):
return str(self.year)
def altered_date(self, this_year):
altered_year = int(this_year)-int(self.year)
return altered_year
This gets the altered year for every First_Year model by calling the function. Unfortunately this function requires an additional parameter - year - so it cannot be called directly from the template. You can create a custom template filter to get around this:
#register.filter
def get_altered_year(obj, gar):
return obj.altered_date(gar)
Now you just need to make sure that you pass year to your view by modifying your return like so:
def cars(request, mark_id, year=year):
...
return render(request, 'cars.html', {'mark':mark, 'year':year, })
And then in your template you can do:
{% for i in mark.groups_set.all %}
{{i.first_year|get_altered_year:year }}
{% endfor %}
You can look at the model method documentation here. And the documentation for custom template filters here.
You mean:
{% for i in mark.groups_set.all %}
{{i.first_year}}
{% endfor %}
This will only allow if groups has a foreign key from mark
Related
First section of code works fine; it is for reference.
#Basic Model
class MyTestModel(models.Model):
record = models.CharField(max_length=100)
def __str__(self):
return self.record
#Specify verbose_name
class Meta:
verbose_name = 'UniqueNameExample'
verbose_name_plural = verbose_name
#Generic ListView.
class MyTemplateView(ListView):
model = MyTestModel
template_name = 'base.html'
context_object_name = 'model_list'
ordering = ['record']
#Python block in HTML template. So far, so good.
{% for item in model_list %}
{{ item.record }}<br>
#{{ item }} also works
{% endfor %}
I am trying to access the Model's verbose_name ('UniqueNameExample') AND the model_list in the view. I've tried registering a filter, a tag, and simple_tag.
Something like: templatetags/verbose.py
from django import template
register = template.Library()
#register.filter (or #register.tag or #register.simple_tag)
def verbose_name(obj):
#Could be verbose_name(model) or whatever input
return obj._meta.verbose_name
And then after
{% load verbose %}
in my HTML (which also works fine), I'll try something like this:
{{ object|verbose_name }}
And I'll get the error 'str' object has no attribute '_meta'. Error is the same if using a tag:
{% verbose_name object %}
Note: tags apparently worked for earlier versions, but maybe I'm using them incorrectly? Not asking to access the Model field verbose_name for "record," btw -- that's answered adequately on SO.
The one thing I've tried that gets the answer half right is if I set the following under MyTemplateView:
queryset = model._meta.verbose_name
The problem with this is it overrides the model_list, and the only result I'm left with is 'UniqueNameExample' without being able to access the record(s) I've used in the model.
I know private=True for _meta (not sure if that's relevant or worth exploring/possibly breaking), but Django admin displays the verbose_name (if set) in the list of created models, so I don't see why I can't do the same (also had a rough time tracing back exactly how it does it in the source code). Maybe it's not a generic ListView but a MixIn? Function-based?
Large(ish) db with thousands of models, each with unique verbose_name[s]; would very much like to keep it simple.
EDIT: Found a fantastic solution from Dominique Barton # https://blog.confirm.ch/accessing-models-verbose-names-django-templates/
First, create a templatags folder at the app level and populate with an init file. Next, create a template tag file. Something like verbose.py.
from django import template
register = template.Library()
#register.simple_tag
def verbose_name(value):
#Django template filter which returns the verbose name of a model.
#Note: I set my verbose_name the same as the plural, so I only need one tag.
if hasattr(value, 'model'):
value = value.model
return value._meta.verbose_name
Next, the ListView should be modified.
from django.views.generic.list import ListView as DjangoListView
from .models import MyTestModel
class ListView(DjangoListView):
#Enhanced ListView which includes the `model` in the context data,
#so that the template has access to its model class.
#Set normally
model = MyTestModel
template_name = 'base.html'
context_object_name = 'model_list'
ordering = ['record']
def get_context_data(self):
#Adds the model to the context data.
context = super(ListView, self).get_context_data()
context['model'] = self.model
return context
Don't forget to add the path to urls.py:
path('your_extension/', views.ListView.as_view(), name='base')
Lastly, load the tag and iterate through the "records" normally:
{% load verbose %}
<h1> {% verbose_name model%} </h1>
<ul style='list-style:none'>
{% for item in model_list %}
<li>{{ item }}}</a></li>
{% endfor %}
</ul>
Pagination also works as advertised.
I've built a for-loop in my HTML template and its nearly working. The issue I'm having is its listing Matches that are apart of a different tour.
I think the way to fix this is adding a filter to the view that basically says "only pull in the matches to do with this tour" which is what I've tried to do below in the Match.objects.filter() but it isnt working and I'm not sure why.
class CricketCalendar(generic.ListView):
template_name="monthly_view/cricket-monthly-view.html"
context_object_name='cricket_monthly_view'
queryset = CricketMonthlyView.objects.all()
def get_context_data(self, **kwargs):
context = super(CricketCalendar, self).get_context_data(**kwargs)
context['Tour'] = Tour.objects.all()
context['Match'] = Match.objects.filter(tour=self.request.Tour)
return context
I have also tried the following and neither worked:
self.kwargs['pk']
self.kwargs['Tour']
Edit, forgot to add the following:
Monthly View models.py:
class CricketMonthlyView(models.Model):
tour = models.ForeignKey('cricket.Tour', on_delete=models.CASCADE,
related_name='tour_name')
match_id = models.ForeignKey('cricket.Match', on_delete=models.CASCADE)
and the URLs.py:
url(r'^monthly-view/$', monthly_view.CricketCalendar.as_view(), name='cricket-monthly'),
Cricket models.py:
class Tour(models.Model):
name = models.CharField(max_length=200)
tier_level = models.ForeignKey('sports.Tier')
country = CountryField()
class Match(models.Model):
tour = models.ForeignKey('Tour', on_delete=models.CASCADE)
And the HTML Template:
{% for match_info in cricket_monthly_view %}
{% for tour in Tour %}
<ul>
<li>{{tour.name}}</li>
</ul>
{% for match in Match %}
<ul>
<li>{{match.home_team}}</li>
<li>{{match.away_team}}</li>
</ul>
{% endfor %}
{% endfor %}
{% endfor %}
This is a great place for adding a break-point. You pretty much want to know the fields on your context, and on self. Add import pdb; pdb.set_trace() in get_context_data, and you'll be able to see the fields on your objects. Use dir(obj) and obj.keys() in order to see all the fields on something.
Alternatively, if you have access to the tour object in your context variable, in your template you can get its matching Matches with tour.match_set.all
Also, be careful about naming the context variable Tour with a capital T, because that's the name of your model.
(I am new to Django)
I'm having a problem with my Django template.
The cruise_details filter should only return one row, but when I try and display this in the template with cruise_details.port for example, nothing is displayed. "code" is correctly getting passed from the URL.
If I remove .port and just put cruise_details I am presented with this on the page
<QuerySet [<Cruise: B724>]>
view.py
def cruise(request, code):
return render(request, 'cruise.html', {
'cruise_details': Cruise.objects.filter(code=code)
})
cruise.html
{{ cruise_details.port}}
models.py
class Cruise(models.Model):
code = models.CharField(max_length=10)
destination = models.CharField(max_length=60)
url = models.URLField
ship = models.ForeignKey(Ship)
duration = models.IntegerField
start = models.DateField
end = models.DateField
added = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
port = models.CharField(max_length=30)
The issue is that Cruise.objects.filter(code=code) returns a list, so if there are many possible matches you can modify your template to show them all
{% for cruise_detail in cruise_details %}
{{ cruise_details.port }}
{% endfor %}
Alternatively, if you know there can only be one result then you can use get instead:
Cruise.objects.get(code=code)
and your existing template should work.
Hope this helps.
I have a class called Features in my models.py. In my html, I am displaying a list on the right that excludes two of these Features, one is the active feature that has been selected, the other is the most recently added since they are the main content of my page. The remaining Features in the list are displayed by date and do show what I am expecting.
Now, I want to single out the first, second and third Features (title only) in THAT list so I can place them in their own separate divs - because each has unique css styling. There are probably numerous ways of doing this, but I can't seem to figure any of them out.
This is a link to my project to give a better idea of what I want (basically trying to get the content in those colored boxes on the right.)
I'm just learning Django (and Python really), so thanks for your patience and help!
HTML
{% for f in past_features %}
{% if f.title != selected_feature.title %}
{% if f.title != latest_feature.title %}
<h1>{{ f.title }}</h1>
{% endif %}
{% endif %}
{% endfor %}
VIEWS
def feature_detail(request, pk):
selected_feature = get_object_or_404(Feature, pk=pk)
latest_feature = Feature.objects.order_by('-id')[0]
past_features = Feature.objects.order_by('-pub_date')
test = Feature.objects.last()
context = {'selected_feature': selected_feature,
'latest_feature': latest_feature,
'past_features': past_features,
'test': test}
return render(request, 'gp/feature_detail.html', context)
MODELS
class Feature(models.Model):
title = models.CharField(db_index=True, max_length=100, default='')
content = models.TextField(default='')
pub_date = models.DateTimeField(db_index=True, default=datetime.now, blank=True)
def __str__(self):
return self.title
def __iter__(self):
return [
self.id,
self.title ]
You can either store the first three Features in separate variables in your context or add checks to your template loop like {% if forloop.first %} or {% if forloop.counter == 2 %}.
If all you want is to not have the
selected_feature
latest_feature
these two records out of the past_features queryset, then you can use exclude on the past_features query and pass the id's of the selected_features and latest_feature objects.
The views.py would look like:
def feature_detail(request, pk):
selected_feature = get_object_or_404(Feature, pk=pk)
latest_feature = Feature.objects.order_by('-id')[0]
# Collect all the id's present in the latest_feature
excluded_ids = [record.pk for record in latest_feature]
excluded_ids.append(selected_feature.pk)
#This would only return the objects excluding the id present in the list
past_features = Feature.objects.order_by('-pub_date').exclude(id__in=excluded_ids)
test = Feature.objects.last()
context = {'selected_feature': selected_feature,
'latest_feature': latest_feature,
'past_features': past_features,
'test': test}
return render(request, 'gp/feature_detail.html', context)
Django provides a rich ORM and well documented, go through the Queryset options for further information.
For access to a specific object in Django templates see following example:
For access to first object you can use {{ students.0 }}
For access to second object you can use {{ students.1 }}
For access to a specific field for example firstname in object 4 you can use {{ students.3.firstname }}
For access to image field in second object you can use {{ students.1.photo.url }}
For access to id in first object you can use {{ students.0.id }}
I'm rather new to Class Based Views, so this is probably obvious, but any tips are appreciated. I want to display "time left" for each item on a list. That is if I have 10 objects, each should display in the template the number of days, hours, mn left until a deadline arrives. Here's my attempt:
model.py
class Law(models.Model):
deadline = models.DateTimeField(_(u'The Deadline'),)
name = ..
more_stuff = ..
views.py
class LawList(ListView):
model = Law
context_object_name = 'law'
template_name = 'template.html'
template.html
{% for l in law %}
<h3>{{ l.deadline }} - {{l.name }} </h3>
{{l.more_stuff}}
{% endfor %}
all good up to here. However I would like to have {{l.time-left}} instead of {{l.deadline}}. Is there a way for the view to calculate this and pass it to the template?
I thought of adding a get_context_data to the 'LawList' view, but I don't know how to do so for every item in my list. Below is what works for a single item.
# views.py, below the section above
def get_context_data(self, **kwargs):
context = super(LawList, self).get_context_data(**kwargs)
context['time_left'] = Law.objects.all()[0].deadline - timezone.now()
but I'm a little stuck. Thanks!
have a look at the timeuntil template tag