Open djjudas21 opened 3 years ago
Hi. Those tags are made to accept both model instances and classes. So if your object
is an instance it should be fine. Have you tried it?
Hi, thanks for your reply. Yes I did try passing in an instance as object
but I think I ran into problems because I was using a custom template tag to wrap up the complexity. This meant that my custom tag was being passed object.elements
, but then inside my custom tag it only had a simple variable like object
, which can't be used directly with {% model_field_help_text %}
because that expects a variable formatted as model.field
.
I hope I've explained this properly. Basically I think my issue is not with getting the right data, but with validation on {% model_field_help_text %}
.
My end goal is to be able to use simple syntax in the main template like this:
<tr>
<td>
{% titlecell object.elements %}
</td>
<td>{{ object.elements }}</td>
</tr>
And then the titlecell
template tag would render this:
{% load model_field %}
<td>
{% model_field_verbose_name from object %}
<br>
<small class="text-muted">{% model_field_help_text from object %}</small>
</td>
Is there an easy way to achieve this? Thanks.
So the behaviour or your custom tag compleately depends on its (tag) implementation.
To implement it properly you may want to take a look at how FieldAttrNode
works.
OK, I've read your code and I don't 100% understand everything, but what I want is to effectively skip the check in https://github.com/idlesign/django-etc/blob/master/etc/templatetags/model_field.py#L94
Logically in my my main template I want to set variable = object.field
, pass variable
into a custom tag, and have that tag render {% model_field_help_text from object %}
, so it has access to everything it needs, just the calling syntax is different.
I want to write as little code as possible to wrap your template tags because anything I write will have to be maintained.
but what I want is to effectively skip the check in
I'd advise to reformulate the task to make it more easy: "make it not to skip but pass the check".
In that case the main objective of your custom tag would be just to accept object
as token
, make it object.elements
, and to pass that object.elements
to FieldAttrNode
as field
argument. That should solve the task.
Good point. This is basically what I've been trying to do with with my experiments with tags and templates, but I haven't managed to make it work yet. Could you give a brief code example, please? I'm trying to understand exactly what you mean - you mean my custom tag would call some of the functions from your module, but not use your tags? Thanks
Could you give a brief code example, please?
Sorry, have work to do these days.
I'm trying to understand exactly what you mean - you mean my custom tag would call some of the functions from your module, but not use your tags
Yeah, if you want a quick solution with code reuse. Basically:
model_field.py
from etc
as such)FieldAttrNode
from etc.templatetags.model_field
FieldAttrNode
with proper args (similar to _get_model_field_attr
)Great, thanks, this helps a lot. I'll have another go tonight when I get off work (sysadmin) :+1:
OK. This is what I've got now:
Detail view template:
...
{% if object.manufacturer is not None %}
<tr>
<td>{% titledescription from object.manufacturer %}</td>
</tr>
{% endif %}
...
And here's my custom tag, which is mostly copied from your function _get_model_field_attr
:
from django import template
from etc.templatetags.model_field import FieldAttrNode
register = template.Library()
@register.tag
def titledescription(parser, token):
tag_name = 'titledescription'
tokens = token.split_contents()
tokens_num = len(tokens)
if tokens_num not in (3, 5):
raise template.TemplateSyntaxError(
'`%(tag_name)s` tag requires two or four arguments. '
'E.g.: {%% %(tag_name)s from model.field %%} or {%% %(tag_name)s from model.field as myvar %%}.'
% {'tag_name': tag_name}
)
field = tokens[2]
as_var = None
tokens = tokens[3:]
if len(tokens) >= 2 and tokens[-2] == 'as':
as_var = tokens[-1]
# FieldAttrNode(field, attr_name, tag_name, as_var)
verbose_name = FieldAttrNode(field, 'verbose_name', 'td') #, as_var)
help_text = FieldAttrNode(field, 'help_text', 'td') #, as_var)
return "{}<br><small class=\"text-muted\">{}</small>".format(verbose_name, help_text)
Rendering this template errors with 'str' object has no attribute 'must_be_first'
and the highlighted line of the template is
{% if object.manufacturer is not None %}
I found a StackOverflow answer which suggests a link to django.template.Node
but that's already imported in your class. Sorry to bug you but I have no idea how to troubleshoot this - thanks.
Sorry for the delay. The following will render help text for elements
attribute when used with {% mytag mymodel %}
.
@register.tag
def mytag(parser, token):
return FieldAttrNode(
field=f'{token.split_contents()[1]}.elements',
attr_name='help_text',
tag_name='mytag'
)
Hope it helps somehow.
Thanks for the explanation. Using your example I can correctly use the tag in the top-scope template like `{% mytag mymodel %}. But I can't get it to work when called from an inclusion template, or a sub template. I want to minimise the number of template tag calls and reuse the formatting, so I want to do maybe this:
@register.tag
def mytag3(parser, token):
help_text = FieldAttrNode(
field=f'{token.split_contents()[1]}.elements',
attr_name='help_text',
tag_name='mytag'
)
verbose_name = FieldAttrNode(
field=f'{token.split_contents()[1]}.elements',
attr_name='verbose_name',
tag_name='mytag'
)
return str("{}<br><small class=\"text-muted\">{}</small>".format(verbose_name, help_text))
Fails with
'str' object has no attribute 'must_be_first'
or this
@register.inclusion_tag('td2.html')
def mytag2(parser, token):
help_text = FieldAttrNode(
field=f'{token.split_contents()[1]}.elements',
attr_name='help_text',
tag_name='mytag'
)
verbose_name = FieldAttrNode(
field=f'{token.split_contents()[1]}.elements',
attr_name='verbose_name',
tag_name='mytag'
)
return {
'help_text': str(help_text),
'verbose_name': str(verbose_name),
}
{{ verbose_name }}
<br>
<small class="text-muted">{{ help_text }}</small>
Fails with
'Format' object has no attribute 'split_contents'
Basically I've got a large table with many rows, and each row is one field. So I don't want each cell title to have manual <small class="text-muted">
etc
I also tried
@register.tag
def mytag4(parser, token):
verbose_name = _get_model_field_attr('model_field_verbose_name', 'verbose_name', token)
help_text = _get_model_field_attr('model_field_help_text', 'help_text', token)
return "{}<br><small class=\"text-muted\">{}</small>".format(verbose_name, help_text)
but it fails with
'str' object has no attribute 'must_be_first'
But I can't get it to work when called from an inclusion template, or a sub template.
as
clause to put tag result into a variable. with
of include
tag to pass this variable to a but subtemplate explicitly.'str' object has no attribute 'must_be_first
Have to idea where and why you address %%must_be_first%% attribute.
Hi there. I'm a django beginner and I'm trying to figure out how to display a field's help_text in a DetailView. I see you've got a template tag that can display the help text based on a
model.field
reference, but I need to display the text based on a field from a model instance object, whereobject
is the entire record andobject.field
is the field.I want to do something like this:
In the wider context of displaying some data in a DetailView template like this:
Is it possible to somehow use the existing tags by referencing an instance, instead of a model? Thanks