Django ManyToMany realtionship doesn't work - python

I'm trying to do this in my models.py:
class Tag(models.Model):
''' snip '''
name = models.CharField(max_length=30)
class Stuff(models.Model):
kind = models.CharField(max_length=30)
tag = models.ManyToManyField(Tag)
But when I make query from Stuff in shell the relationship fields return 'None' like this:
>>> q = Stuff.objects.all()
>>> p = q.tag.name
>>> print q.tag.name
None
I can't use this keys in my template too.
Database backend is mysql.
What is the problem?

You can call many to many relation like this,
q = Stuff.objects.all()
for p in q.tag.all():
print p.name
In HTML
{%for tag in stuff.tag.all %}
{{ tag.name }}
{% endfor %}

It's unclear how exactly you want your models to work. Do you want Stuff to have a single Tag or several Tags?
Solutions for both case uses follow.
The case where a Stuff object has many Tags
There are some mistakes here.
In your example the variable q isn't a Stuff object, so you can't ask for it's attributes. It's a queryset. Like the other answer exemplifies you have to iterate through it, like a list.
A ManyToMany relationship means that Stuff is going to have many Tags, but you're using it as if Stuff had only one Tag.
An example (I'm going to replace Stuff.tag by Stuff.tags, since it's misleading to call it a single tag):
# Get the first stuff
>>> stuff = Stuff.objects.first()
# Access the attribute `tag`. Notice it's NOT a Tag, but a `RelatedManager`.
>>> stuff.tag
<django.db.models.fields.related.ManyRelatedManager object at 0x7fe2a3e5cc10>
# which you can use as a queryset!!
>>> stuff.tag.all()
[<Tag: tag1>, <Tag: tag2>, ...]
# Then you can iterate through it, filter or whatever
>>> stuff.tag[0]
<Tag: tag1>
>>> stuff.tag[0].name
u'tag1'
The case where a Stuff object has a single Tag
If you want Stuff to have only ONE Tag object you have to declare it like this.
tag = models.OneToOneField(Tag)
NOW you can do stuff.tag.name and use it like that in the template. If you want many Tags you'll have to iterate through the Stuff's Tags like this (again, I'm using tags instead of tag for this example):
{% for tag in stuff.tags %}
{{ tag.name }}
{% endfor %}
There are of course other ways, like using the filter join
{# Print the tag names joined by ', ' #}
{{ stuff.tags.all|join:', ' }}

Related

Check if list of objects contains an object with a certain attribute value in django template tags

I want to check if my list of objects contains an object with a certain attribute value in Django template tags. As we know it is represented in Python like:
any(x.name == "t2" for x in l)
So, is there some tags to express this in template tags something like:
{% if any x.atype == "Other" for x in list %}
{% endif %}
or something else do that?
There is no build-in tag for this, but you can create your own: add templatetags python directory to your project app where you plan to use tag. Then add file called tags.py and insert code listed below (example):
from django import template
register = template.Library()
#register.simple_tag(name=any)
def any_of_following(objects, value):
return any([item for item in objects if item == value])
Then you can use this tag in template as following:
{% any your_objects value_to_check %}
You can read more about simple_tags in Django docs

How to query in django one to many

What I want to do?
I have an app have three models:Series,Section and Episode,every one have a one-many query(by ForeignKey). just like this
series-> many Section,section-> many Episode
Now I will show a section with episodes information in series page, but it make more query.
now code
views.py
series = Series.objects.get(id=series_id)
section = Section.objects.filter(series=series)
list.html
{% for item in sections %}
...
{% for episode in item.episode_set.all %}
...
{% endfor %}
...
{%endfor%}
models.py
class Section(models.Model):
series = models.ForeignKey(Series)
....
class Episode(models.Model):
section = models.ForeignKey(Section)
What I want to get ?
an example code tell me how to query in views.py and use just little query.
you can guess, in my code, if here are many section and many episode, it will have many query.
Some idea.
I use Laravel Before, In Laravel , it has a hasMany Function, I can use it to get other models items(Define by belongsTo).
Is Django has same function?
If in template you need only episodes, you can query just them with single query
Episode.objects.filter(section__series=series_id)
But if you need all data then you can use prefetch_related Django docs.
The simplest example looks enough for your code
series = Series.objects.filter(id=series_id).prefetch_related('section_set', 'section_set__episode_set').get()
After this calls to series.section_set.all() and to section.episode_set.all() will not produce extra query.

Django's template language can not safely process dict

In django's template language, if a variable called a.x, then django will try in this order to get the right value:
a['x'], dictionary lookup
a.x or a.x(), attribute or method lookup
a[x], list indexing
The result is: any variable that support var['str'] becomes a time bomb. Imagine a is a dict, with a key 'items' whoes value is 'hello world', then a.items will result in 'hello world', not ditc.items(). Here is the code:
from django.template import Engine, Context
template = """
{% for k, v in a.items %}
{{ k }} = {{ v }}
{% endfor %}
"""
e = Engine()
t = e.from_string(template)
a = {'items': 'hello world'}
print(t.render(Context({'a': a})))
While I expect items = hello world as the output, the real output is:
h =
e =
l =
...
For a dict variable, if it has a key with the same name of a dict method, then you will never be able to call that method in the template. It becomes a time bomb because you never know if in the future you will add a key called 'items' into a dict variable.
Such risk is not only for dict but any type which accept var['key'].
So my question is, why the order is designed like this, and how to make it safe.
There is a simple workaround (besides using a better name for your keys):
from django.template import Engine, Context
template = """
{% for k, v in items_func %}
{{ k }} = {{ v }}
{% endfor %}
"""
e = Engine()
t = e.from_string(template)
a = {'items': 'hello world'}
print(t.render(Context({'a': a, 'items_func': a.items})))
>> items = hello world
why the order is designed like this
It's designed like this because the template syntax is supposed to be simple and doing so encourages you to put more logic into the view rather than in the template.
How to make it safe?
Give your keys a descriptive name
"items" doesn't do anything to actually describe what should be inside of the key, so not only will it give django a headache, it will give any other developer that has to debug an issue with this dictionary a headache too.
By using the . for three different lookups, there's always the potential for a problem like this. If the order was changed to try a.x first, then it would cause problems for other users that wanted to access a['x'].
I don't think there's a general way to protect against this behaviour. Once you're burned by it once, you'll remember not to put an items key in your dictionaries in future.
As a last resort, you could switch to Jinja, which supports subscript syntax, {{ foo['bar'] }}. That means that {{ foo.bar }} checks for the attribute foo.bar first. See the Jinja docs for more information.

Allowing <br> tags with Google App Engine and Jinja2

In my web app, the user can make blog posts. When I display the blog post, newlines aren't shown because I didn't replace the new lines with <br> tags. The problem is that I've turned autoescaping on in Jinja, so <br> tags are escaped. I don't want to temporarily disable autoescaping, I want to specifically allow <br> tags. How would I do this?
I have another answer that I think is the best. Initially I was just displaying my variable post.content as-is, and the newlines weren't being preserved. None of the solutions here worked (well), and my pre solution was just a quick fix and had major issues. This is the real solution:
{% for line in post.content.splitlines() %}
{{line}}<br>
{% endfor %}
You can use the |safe filter, or use the autoescape blocks:
{% autoescape false %}
{{ content goes here }}
{% autoescape %}
You could also set autoescaping in the environment to False.
In your model object, add a function like this:
class Post(db.Model):
# ...
def html_content(self):
# Escape, then convert newlines to br tags, then wrap with Markup object
# so that the <br> tags don't get escaped.
def escape(s):
# unicode() forces the conversion to happen immediately,
# instead of at substitution time (else <br> would get escaped too)
return unicode(jinja2.escape(s))
return jinja2.Markup(escape(self.content).replace('\n', '<br>'))
Then in your template, just call that:
<p>{{ post.html_content() }}</p>
You can create a jinja2 filter:
import re
from jinja2 import evalcontextfilter, Markup, escape
_paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}')
#evalcontextfilter
def nl2br(eval_ctx, value):
result = u'\n\n'.join(u'<p>%s</p>' % p.replace('\n', '<br>\n')
for p in _paragraph_re.split(escape(value)))
if eval_ctx.autoescape:
result = Markup(result)
return result
You need to add the filter to your jinja2 Environment before you can use it:
JINJA2_ENV.filters['nl2br'] = jinja2_filters.nl2br
In your template you can use that filter:
{{post.content|nl2br}}
Here's a filter wrote by myself:
import jinja2
#jinja2.evalcontextfilter
def nl2br(eval_ctx, value):
result = jinja2.escape(value).unescape().replace('\n', '<br>')
if eval_ctx.autoescape:
result = jinja2.Markup(result)
return result
And add the filter to the jinja2.Environment() by calling:
jinja_env.filters['nl2br'] = nl2br
Note that i have autoescape on by default, so I don't check it in this function, but this is what I'm using
def nl2br(value):
split = value.split('\n')
return jinja2.Markup('<br>').join(split)
then of course,
jinja_env.filters['nl2br'] = nl2br
The solution was to put <pre></pre> tags around the area where I had the content.
The easiest way to do this is to escape the field yourself, then add line breaks. When you pass it in in jinja, mark it as safe so it's not autoescaped.

More than one index field in self.prepared_data

I'm using Haystack and Whoosh with Django and I was trying to create the indexes through the code below:
class LivroIndex(SearchIndex):
text = CharField(document=True, use_template=True)
autor = CharField(model_attr='Autor')
titulo = CharField(model_attr='Titulo')
datalivro = DateTimeField(model_attr='DataLivro')
def index_queryset(self):
return Livro.objects.filter(DataLivro__lte=datetime.datetime.now())
def prepare(self, obj):
self.prepared_data = super(LivroIndex, self).prepare(obj)
self.prepared_data['text'] = obj.Autor
return self.prepared_data
Livro is a class in my models file.
In this way, you can use only "autor" as a field which users can use to search content inside the application. Is there any other ways to make it accept more than one field?
Thanks in advance!
use template and add which fields you want to be found in the search
like
{{ object.autor }}
{{ object.titulo }}
{{ object.datalivro }}
You're supposed to concatenate all the fields you want to search on in the text field. Usually you do this via a template, rather than via the prepare method. See the tutorial for instructions.

Categories