Django define m2m field conversion (from to string) - django

I'm trying to work with m2m fields.
What I want to do is to have a string (CharField) where user can write the tags of a post, with each tag separated by commas.
I was able to do the creation in this way:
tags = tags.split(',')
for tag in tags:
obj, create = Tag.objects.get_or_create(name=tag)
pub.tags.add(obj)
Now, I want to do the UpdateView. Obviously if I don't specify the conversion from list to string in the form, I don't have any value set. So it should be something like:
for tag in tags:
str+=tag+","
The point is:
Do I have to write the conversion of list to string and string to list each time?
Can i specify somewhere how to do this conversion? Is there anything already implemented in Django?
PS: In the UpdateView, if I remove a tag, how can I remove it from the relation as well since I have to do the parsing by hand?
Thanks.

The simplest way is to remove all the tags from pub.tags first, then add them all back in:
# Clear the existing tags
pub.tags.clear()
tags = tags.split(',')
for tag in tags:
obj, create = Tag.objects.get_or_create(name=tag)
pub.tags.add(obj)
Instead of looping and building a String, you might try this more pythonic method:
tags = ['red', 'green', 'blue']
','.join(tags)
'red,green,blue'

Related

django dynamic content in query

I am trying to "DRY" my code and would like to create a function to make my query dynamic.
The code that I currently use is :
rightone = []
for item in taglist: #taglist is a list of model instances, not relevant here
content = content.filter(tags__title=item.title) #tags here is a M2M key : my problem, content is a query
rightone.append(content)
tagproofquery = rightone[-1]
and I would like to convert it to:
def uniquetogether(queryset,data, model):
rightone = []
for item in queryset:
content = data.filter(tags__title=item.title) # <--- problem is here with tags
rightone.append(content)
tagproofquery = rightone[-1]
return tagproofquery
I have no idea how to replace my M2M "tags" as in tags__title=item.title with the "model" parameter of my function. I tried f strings but it failed miserably (of course).
Is there a way to do this? Many thanks

Wagtail: Filter Page model by a field on the child

I have two models, ParentPage and ChildPage. I want to find the set of ParentPages where a field is_completed is True on the ChildPage.
Normally in Django, I could do something like
ParentPage.objects.filter(child_page__is_completed=True)
However, I don't think there is a join here for the Wagtail/Treebeard hierarchy.
I also thought you might be able to filter ChildPages by multiple ParentPages, e.g. ChildPage.objects.children_of([parent_ids]), but I can't see a way to do that either.
Is there a simpler way?
The Page table has path and url_path columns. If you find all children and strip the last part of the path or url_path, you can use that result to query the parent pages.
Path:
child_paths = ChildPage.objects.filter(is_completed=True).values_list("path", flat=True)
parent_paths = set([cp[:-4] for cp in child_paths])
pages = Page.objects.filter(path__in=parent_paths)
Url path:
child_urls = ChildPage.objects.filter(is_completed=True).values_list("url_path", flat=True)
parent_urls = set([url.rsplit('/', 1)[0] for url in child_urls])
pages = Page.objects.filter(url_path__in=parent_urls)
Disclaimer: untested code.

Searching through a ManyToManyField in Django

I have a model that contains a ManyToManyField. It looks like this..
applicable_events = models.ManyToManyField(Event)
I am trying to basically search using something like this:
if 'Video' in prop.applicable_events.all():
print("here")
But it isn't fully working as I expected. I want it to search that applicable_event (which is another model). The applicable_event model contains a field named 'name' which I am trying to search against.
If I do something like this
print(prop.applicable_events.all().filter(name=cur_event))
It prints
<QuerySet [<Event: Video Sign In Started>]>
So basically I am trying to find out if the string 'Video' is contained in that.
You can search if such event exists with:
if prop.applicable_events.filter(name__icontains='Video').exists():
# …
pass
or if you want an exact name match:
if prop.applicable_events.filter(name='Video').exists():
# …
pass
The reason 'Video' in prop.applicable_events.all() does not work is because that is a QuerySet (a collection) of Events, a string like 'Video' is not the same as an Event with that name, and definitely not if 'Video' is a substring of the name.

Django get_or_create with icontains

I'm getting an unexpected result using icontains in my get_or_create call.
Take the following example:
>>>team_name = "Bears"
>>>Team.objects.get(name__icontains=team_name) # returns DoesNotExist as expected
>>>team, created = Team.objects.get_or_create(name__icontains=team_name)
>>>print(created) # Prints True as expected
>>>print(team.name) # Prints an empty string!
Why does this create a team with a blank name rather than "Bears"? The reason I'm using get_or_create here is that if a subsequent user posts something like "BearS" I want to get the correct team, not create a duplicate team with incorrect capitalization.
I think here you should split the get() and create() functionalities instead of using get_or_create(), because the __icontains lookup works for get() only.
Try doing something like this:
>>> team_name = 'Bears'
>>> teams = Team.objects.filter(name__icontains=team_name)
# This will filter the teams with this name
>>> team = teams.first() if teams.exists() else Team.objects.create(name=team_name)
# Now your team is the first element of your previous query (it returns a QuerySet with single element) if it exists
# Otherwise, you create a new Team.
Another option besides wencakisa's answer is to include the defaults parameter in get_or_create, because Django strips lookups containing the __ separator. See answers to this question.
The code would be:
Team.objects.get_or_create(
name__icontains=team_name,
defaults = {
"name": team_name
}
)
The right way to do it is using Django's function get_or_create(). But instead of "icontains", you should use "iexact" (), unless you want an exact match, in wich case you should use just "exact":
Team.objects.get_or_create(
name__iexact=team_name,
defaults = {
"name": team_name
}
)
Outside "defaults" you should put your search terms. If the objects doesn't exist, you should write your creation terms inside 'defaults'

How to access an integer key inside a django template if the dict has a same string key?

Suppose I've a dict like:
dic = {'1': 'string', 1 :'integer'}
When I pass it to a django template and try to access dic.1 then it always returns 'string'.
If I remove the key '1', then dic.1 returns 'integer'.
I know I can use a custom tag for this, something like:
from django import template
register = template.Library()
#register.filter
def get_key(value, arg):
return value.get(arg, None)
Then {{ dic|get_key:1 }} works fine.
But, is there a way to directly access the integer/float keys without using a custom tag?
Refer to the answer provided for the question on the topic of Dictionary access speed comparison with integer key against string key. Python (CPython) will always first try to find the string-based key before attempting to find any alternative.
You might have a very good reason to mix similar string keys with integer keys, but I would recommend avoiding this technique or type-casting the integers to strings to eliminate the inability to directly reference the key values.