Using Django ~=1.11 and Python 3.6
I am a beginner! Every answer I've found online for my question is more advanced than what I'm looking for.
Here's my model:
class Byte(models.Model):
text = models.CharField(max_length=30)
def __str__(self):
return self.text
Here's my view:
def byte_list(request):
bytes = Byte.objects.order_by('text')
return render(request, 'cloudapp/byte_list.html', {'bytes': bytes})
Here's my template:
{% block content %}
<div class="total">
<h2>Total number of words and phrases entered: {{ byte.count }}</h2>
</div>
<hr>
{% for byte in bytes %}
<div class="byte">
<h2>{{ byte.text }}</h2>
</div>
{% endfor %}
{% endblock %}
This allows the creation "Byte" objects in the /admin, with only one field - a small text field. Right now, the template simply displays a list of all the objects created.
Question/Problem: I'd like to display the total number/count of objects that have been created for the Byte model. In the template, I have a tag {{ byte.count }} to display this.
I've tried using count() and Aggregation, but not sure how to work those into my model/view/template. I'm looking for the most simple and up-to-date way to accomplish this, whether it's using a method or #property in the model, or some type of query set in the view.
You've got a few different options... the most common ways to get the total number of model instances I have seen are:
my_total = len(Byte.objects.filter())
or, without having to run the full query:
my_total = Byte.objects.count()
Here's a link to the resource doc for 1.11: https://docs.djangoproject.com/en/1.11/topics/db/aggregation/#cheat-sheet
There's nothing wrong with Exprator's answer, but one alternative is to use the built in length template filter:
<h2>Total number of words and phrases entered: {{ bytes|length }}</h2>
If you're not planning to iterate over the bytes queryset you could also call count on it directly in the template:
<h2>Total number of words and phrases entered: {{ bytes.count }}</h2>
That will force a second database query, though, so only do that if you aren't otherwise causing bytes to be evaluated.
The decision of what to put in the view and what to do with template filters/no-arg methods is more a question of style than a hard and fast rule. Erring on the side of using the view is usually right, here it's simple enough that I might just do it in the template.
def byte_list(request):
byte= Byte.objects.count()
bytes = Byte.objects.order_by('text')
return render(request, 'cloudapp/byte_list.html', {'bytes': bytes,'byte':byte})
And in template
{{ byte }}
Related
I have Django model with CharFields 'flname1', 'date1', and 'time1'. My goal in my HTML is to have a {{ forloop }} that runs through only the 'date1' and 'time1' fields and displayed all of them. My Problem is that in my views file I can't find a way to create a python variable that only contains two of the three fields from one model. Ie tried a lot but what I'm trying to avoid is...
posts = DocPost.objects.all()
This puts all three fields into a variable and displays them in my for loop which I don't want. I've also tried a lot of work with filters and go things that other people on the internet had success with that didn't work for me like...
posts = DocPost.objects.filter('flname1').only('date1', 'time1')
This didn't work and didn't section off date1 and time1 and pack them away in a variable that I could loop through. Ive tried a lot more than this at this point to no prevail. Thank for any help.
There are two things you can do to only get certain fields in a query and iterate over them. The template for both is pretty much the same
First, you can use only() to generate a queryset where each object only has certain fields populated and all the rest are deferred
# View
context['posts'] = DocPost.objects.only('date1', 'time1')
# Template
{% for post in posts %}
{{ post.date1 }}
{{ post.time1 }}
{% endfor %}
Second, you can use values() to generate a queryset of dictionaries that only contain the fields specified
# View
context['posts'] = DocPost.objects.values('date1', 'time1')
# Template
{% for post in posts %}
{{ post.date1 }}
{{ post.time1 }}
{% endfor %}
In Wagtail, I have made a Block with an ImageChooserBlock in it like this:
class MyBlock(blocks.StructBlock):
background = ImageChooserBlock()
Now i want to add some extra fields to the ImageChooserBlock so I moved it to its own Block so now it looks like this:
class FancyImageChooserBlock(ImageChooserBlock):
extra = blocks.Charfield()
class MyBlock(blocks.StructBlock):
background = FancyImageChooserBlock()
My first issue is that the extra field doesn't get included. (Maybe because the block inherits from ImageChooserBlock?
My second and most important issue is that I want to able to have the extra field to be hidden in the form, but included in the template rendering. Does someone know how and if this is possible? I don't want to do any hacky stuff injecting js or css for this. There must be a way to do this using Blocks, Widgets and forms.HiddenInput or something like that.
I know i can do some calculations in the clean method of my FancyImageChooserBlock to manually set the value of extra. This is exactly what I want to do.
Any help is appreciated, I'm really stuck here.
ImageBlockChooser isn't like 'StructBlock' or 'ListBlock' or 'StreamBlock' which are all structural block types - that is which are designed to 'look for' any child fields you might define. Only the structural block types are prepared for that 'out of the box'. For a block to use fields it needs to be configured to use/generate a template with those fields.
Personally, I think there are better ways of achieving what you want than to subclass ImageChooser as it will generally be more robust to try and find ways of using the features that Wagtail provide, even if you have to be a bit creative with them.
However, in case you still want to know how it might be done (hacked) by subclassing ImageChooser:
SOLUTION 1 - subclassing ImageChooser (this wouldn't be my preferred option for this):
#the chooser block class:
class MyImageChooserBlock(ImageChooserBlock):
#cached_property
def widget(self):
from .mywidgetsfolder import MyAdminImageChooser
return MyAdminImageChooser
from wagtail.admin.widgets import AdminChooser
from wagtail.images import get_image_model
#the chooser admin class...
class MyAdminImageChooser(AdminChooser):
"""the only difference between this class and AdminImageChooser
is that this one provides a different template value to
render in the render_to_string method and the addition of certain
variables to the attrs dictionary. You could probably get away
with subclassing AdminImageChooser instead of AdminChooser and just
overriding the render_html method, but for some reason that seemed
to give me a duplicate 'choose image' button and it didn't seem
essential to fix it to demonstrate this principle"""
choose_one_text = _('Choose an image')
choose_another_text = _('Change image')
link_to_chosen_text = _('Edit this image')
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.image_model = get_image_model()
def render_html(self, name, value, attrs):
instance, value = self.get_instance_and_id(self.image_model,
value)
attrs['extra_hidden_fields'] = ('extra_1', 'extra_2')
original_field_html = super().render_html(name, value, attrs)
return render_to_string("my-widgets-folder/my_image_chooser.html", {
'widget': self,
'original_field_html': original_field_html,
'attrs': attrs,
'value': value,
'image': instance,
})
def render_js_init(self, id_, name, value):
return "createImageChooser({0});".format(json.dumps(id_))
#my-widgets-folder/my_image_chooser.html template:
{% extends "wagtailadmin/widgets/chooser.html" %}
{% load wagtailimages_tags %}
{% block chooser_class %}image-chooser{% endblock %}
{% block chosen_state_view %}
{% for a in attrs.extra_hidden_fields %}
<input type="hidden", name={{a}}, value="">
{% endfor %}
<div class="preview-image">
{% if image %}
{% image image max-300x300 class="show-transparency" %}
{% else %}
<img>
{% endif %}
</div>
{% endblock %}
{% block edit_chosen_item_url %}{% if image %}{% url 'wagtailimages:edit' image.id %}{% endif %}{% endblock %}
SOLUTION 2 - using a custom struct block and the group meta value:
This solution uses a custom struct block to hide the fields and labels. I'd recommend this approach for now as it uses customisation features provided by wagtail for your use. Although the documentation does not mention using the group meta in this way the group meta is documented and should be ok to rely on (and it could probably be quite easily replaced with another detail if necessary).
For anyone happening on this answer I'd suggest checking the wagtail docs before using this as it seems development on the project is fast and I wouldn't be at all surprised if they offered a 'built in' way of generating hidden fields soon.
In my own tests I didn't get the indent mentioned by the OP in his comment. As long as the struct is itself a top level element all the children were left aligned by default - so they were aligned with any fields outside of the struct block.
Of course you can create custom blocks of the basic types (say a custom CharBlock) and specify the widget kwarg as forms.HiddenInput but you still have the label to deal with - and passing a classname kwarg only applies it to the input not the label etc. Also using custom basic blocks means keeping them forever or providing deconstruct methods to avoid migration trouble. This circumvents all these sorts of issues.
Of course any of this could be achieved easily with some JS/css but this is offered assuming we want an html only solution.
class StructWithHiddenFields(StructBlock):
classMeta:
form_template = "blocks/admin/struct_with_hidden_fields.html"
"""Obviously you'd want to copy the template from the wagtail one
for StructBlocks (wagtailadmin/block_forms/struct.html) to ensure
similar behaviour and then add a bit of logic for the hiding.
This might look like this:"""
#blocks/admin/struct_with_hidden_fields.html template:
<div class="{{ classname }}">
{% if help_text %}
<div class="sequence-member__help help"><span class="icon-
help-inverse" aria-hidden="true"></span>{{ help_text }}
</div>
{% endif %}
<ul class="fields">
{% for child in children.values %}
{% if child.block.meta.group != "hidden-input" %}
<li{% if child.block.required %} class="required"{% endif %}>
{% if child.block.label %}
<label{% if child.id_for_label %} for="{{ child.id_for_label }}"{% endif %}>{{ child.block.label }}:</label>
{% endif %}
{{ child.render_form }}
</li>
{% endif %}
{% endfor %}
</ul>
{% for child in children.values %}
{% if child.block.meta.group == "hidden-input" %}
<input type="hidden" id="{{ prefix }}-{{child.block.label}}" name="{{ prefix }}-{{child.block.label}}" value="{{child.block.value}}">
{% endif %}
{% endfor %}
</div>
#Usage:
class MySpecificBlockWithHiddenFields(StructWithHiddenFields):
normal_field = CharBlock(required=False)
hidden_field = IntegerBlock(required=False, group="hidden-input")
Regarding your first question, why not just:
class MyBlock(blocks.StructBlock):
background = ImageChooserBlock()
extra = blocks.Charfield()
?
Sweet solution in the model
This answer isn't really the answer on the question but a better option to go with when trying to add some fields to the ImageChooser on the background. As learned from the Wagtail docs there is this thing called the Custom Image Model
So instead of trying to add the fields on the Block "layer", I added them on the Model. For me the code looks something like this:
class ImageModel(AbstractImage):
extra = models.CharField(max_length=255, blank=True, null=True)
admin_form_fields = Image.admin_form_fields # So that in the image edit page, the fields are shown
def save(self, **kwargs):
self.clean_extra()
return super(ImageModel, self).save(**kwargs)
def clean_extra(self):
if something():
extra = 'This gets added as an attribute of image'
# Needed for the rendition relation
class ImageRenditionModel(AbstractRendition):
image = models.ForeignKey(ImageModel, related_name='renditions')
class Meta:
unique_together = (
('image', 'filter_spec', 'focal_point_key'),
)
Also, the WAGTAILIMAGES_IMAGE_MODEL must point to your own model, more about this you can find in the docs.
A very hacky solution in the blocks
There is another way to do this, without using extra html or extra models, but it is very hacky and disapproved.
class FancyImageChooserBlock(ImageChooserBlock):
def clean_extra(self, value):
if something():
value['extra'] = 'This will get rendered in template'
return value
# for rendering in preview
def clean(self, value):
value = super(FancyImageChooserBlock, self).clean(value)
value = self.clean_extra(value)
return value
# for rendering the live view
def to_python(self, value):
value = super(FancyImageChooserBlock, self).to_python(value)
value = self.clean_extra(value)
return value
This way you don't need extra html, css or js when adding an extra value. The point why this is disencouraged is because it takes up a lot of UX performance and also overriding the to_python function and clean function to inject an extra variable is very, very hacky and dirty as can be. But it works, so if you don't mind design standards or performance, work alone and nobody else will ever, ever see your code, you could use this.
So don't..
I'm trying to filter objects across two templates. One (the parent) should display the five most recently updated, and the other should display all of them.
I have the latter working perfectly with the following code:
views.py:
...
class ChannelProjectList(generic.ListView):
context_object_name = 'projects_by_channel'
template_name = 'channels/projects_by_channel.html'
def get_queryset(self):
self.channel = get_object_or_404(Channel, slug=self.kwargs['slug'])
return Project.objects.filter(channel=self.channel)
...
HTML:
{% for project in projects_by_channel %}
{{project.name}}
{% endfor %}
But when I go to "include" it on the parent page it breaks. After some research I understand why that is happening and why that isnt the proper way to do it. I dug around and found this, which seems to be exactly what I'm trying to do but when I implemented it, not only did it not work it but also broke the page that is working.
This feels like a pretty simple thing, but since this is my first project I'm running into new things every day and this is one of them.
Final Solution:
With the help of this I realised I needed to copy in the same get_queryset into the second template view, which I was then able to call into the template using "view.channel_projects"
You have two possibilities. First you could define two context variables (like its done in your linked solution) or you could slice the qs in the template.
1. Option slice:
This one would display all:
{% for project in projects_by_channel %}
{{project.name}}
{% endfor %}
This on only displays 5 entries:
{% for project in projects_by_channel|slice:":5" %}
{{project.name}}
{% endfor %}
2. Option define two query sets:
(views.py)
def get_queryset(self):
self.channel = get_object_or_404(Channel, slug=self.kwargs['slug'])
self.channel2 = get_object_or_404(Channel, id=12)#whatever
context["list"] = Project.objects.filter(channel=self.channel)
context["list2"] = Project.objects.filter(channel=self.channel2)[0:5] #this slices the query set for the first entries. If you want to order them first(by date or whatever) use "order_by()"
return context
(html)
{% for project in list %}
{{project.name}}
{% endfor %}
{% for project in list2 %}
{{project.name}}
{% endfor %}
If you want to display a single qs but one time the whole thing and in another template just the first 5 you are better suited with using the slice argument in the template. It keeps the view clean and simple and you don't have to query two times.
I hope that helps if not leave a comment.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
how to create counter loop in django template?
I want to print some data based on some condition,
I want to use it like we used in other languages:
for(i=1;i<=count;i++)
print i
To do this in django I wrote
{% for i in count %}
<p>{{ i }}</p>
{% endfor %}
but it gives me an error 'int' object is not iterable.Count is coming from views.py and if I prints the count alone than it shows the output.
I wanted to print some value until count not becomes zero,So how i can do this in django.
And one more thing can we use while loop in django because i also try to use it for this task but it gives me the error of invalid block tag: 'while'
So please let me know how can I do this task ?
Thanks
Edit
in my view.py I have used like this
count=Product_attributes.objects.count()
and then pass this count to my template
Django templates are not programming language. Write all you logic in the view or models, and pass data into the template:
def view(request):
values = []
for i in range(10):
values.append(i) # your custom logic here
return render_to_response("/path/to/template", {'values': values})
in template:
{% for value in values %}
<p>{{ value }}</p>
{% endfor %}
The "for i in var" syntax works only where "var" is an iterable eg a list, tuple, set, dict...
I'd suggest the following: Instead of passing the item count to the template, pass in the iterable eg list in. If all you have is a count, you can create an iterable using range(count) in the view. In code
# Extract from view
def view(request):
# Set up values. Values is a list / tuple / set of whatever you are counting
values = Product_attributes.objects.all()
return render_to_response("/path/to/template", {'values': values})
# Extract from template
{% for value in values %}
<p>{{value}}</p>
{% endfor %}
The "while" tag is not a valid built in tag in django. A list of valid built-in tags can be seen here: https://docs.djangoproject.com/en/dev/ref/templates/builtins/
This way of doing things is not specific to templates only: it has parallels in "regular python" where the canonical way to iterate over a collection is:
for item in iterable:
# do something with the item
pass
More information on the "python way" of doing for loops can be found here: http://wiki.python.org/moin/ForLoop
If it's not appropriate to add the range to your view code (I don't like adding purely template-ty things to my views), I'd probably create a range filter:
#register.filter
def range(val):
return range(val)
Then you'd use it like this:
{% for i in count|range %}
<p>{{ i }}</p>
{% endfor %}
There's also an extremely ugly hack you can use if you don't want to bother writing any python code for this, it takes advantage of Django's center (or ljust and rjust) filters which create a string of length of the value provided. You can use it like this:
{% for x in count|center:count %}
<p>{{ forloop.counter }}</p>
{% endfor %}
I wouldn't recommend doing it this way though, I'm just demonstrating that it's possible.
How can you perform complex sorting on an object before passing it to the template? For example, here is my view:
#login_required
def overview(request):
physicians = PhysicianGroup.objects.get(pk=physician_group).physicians
for physician in physicians.all():
physician.service_patients.order_by('bed__room__unit', 'bed__room__order', 'bed__order')
return render_to_response('hospitalists/overview.html', RequestContext(request, {'physicians': physicians,}))
The physicians object is not ordered correctly in the template. Why not?
Additionally, how do you index into a list inside the template? For example, (this doesn't work):
{% for note_type in note_types %}
<div><h3>{{ note_type }}</h3>
{% for notes in note_sets.index(parent.forloop.counter0) %}
#only want to display the notes of this note_type!
{% for note in notes %}
<p>{{ note }}</p>
{% endfor %}
{% endfor %}
</div>
{% endfor %}
Thanks a bunch, Pete
As others have indicated, both of your problems are best solved outside the template -- either in the models, or in the view. One strategy would be to add helper methods to the relevant classes.
Getting a sorted list of a physician's patients:
class Physician(Model):
...
def sorted_patients(self):
return self.patients.order_by('bed__room__unit',
'bed__room__order',
'bed__order')
And in the template, use physician.sorted_patients rather than physician.patients.
For the "display the notes of this note_type", it sounds like you might want a notes method for the note_type class. From your description I'm not sure if this is a model class or not, but the principle is the same:
class NoteType:
...
def notes(self):
return <calculate note set>
And then the template:
{% for note_type in note_types %}
<div><h3>{{ note_type }}</h3></div>
{% for note in note_type.notes %}
<p>{{ note }}</p>
{% endfor %}
</div>
{% endfor %}
"I'd like to do this from within a template:"
Don't. Do it in the view function where it belongs.
Since the question is incomplete, it's impossible to guess at the data model and provide the exact solution.
results= physician.patients.order_by('bed__room__unit', 'bed__room__order', 'bed__order')
Should be sufficient. Provide results to the template for rendering. It's in the proper order.
If this isn't sorting properly (perhaps because of some model subtletly) then you always have this kind of alternative.
def by_unit_room_bed( patient ):
return patient.bed.room.unit, patient.bed.room.order, patient.bed.order
patient_list = list( physician.patients )
patient_list.sort( key=by_unit_room_bed )
Provide patient_list to the template for rendering. It's in the proper order.
"how do you index into a list inside the template"
I'm not sure what you're trying to do, but most of the time, the answer is "Don't". Do it in the view function.
The template just iterate through simple lists filling in simple HTML templates.
If it seems too complex for a template, it is. Keep the template simple -- it's only presentation. The processing goes in the view function
You should be able to construct the ordered query set in your view and pass it to your template:
def myview(request):
patients = Physician.patients.order_by('bed__room__unit',
'bed__room__order',
'bed__order')
return render_to_response('some_template.html',
dict(patients=patients),
mimetype='text/html')
Your template can then loop over patients which will contain the ordered results. Does this not work for you?
EDIT: For indexing, just use the dot syntax: mylist.3 in a template becomes mylist[3] in python. See http://docs.djangoproject.com/en/dev/ref/templates/api/#rendering-a-context for more information.
This is one way of doing it, although very ugly :
{% for note in note_sets|slice:"forloop.counter0"|first %}