Save data to ManyToManyField error in django? - python

this is the model
class Question(models.Model):
question = models.CharField(max_length = 200)
questionbody = models.TextField()
author = models.ForeignKey(User)
tags = models.ManyToManyField(Tag)
timestamp = models.DateTimeField('question post date')
and this is the view.py
stags = request.POST['tag']
ltags = stags.split(',')
q = Question(
question = request.POST['question'],
questionbody = request.POST['questionbody'],
author = request.user,
timestamp = datetime.datetime.now()
)
q.save();
for i in ltags:
# print i ouyput:jquery
# print type(i) ouyput:unicode
# s = Tag.objects.get(name=i)
s = Tag.objects.get(name='jquery')
q.tags.add(s)
as u can see the 'tag' input is in 'string , string' format string, so i change it to a list (ltags),
but in the for loop when i try to add the tags like
s = Tag.objects.get(name=i)
to question,there is an error says
" Tag matching query does not exist ".
but when i use s = Tag.objects.get(name='jquery'), it works.
please help!

my bad, it turns out the 'i' does contain a trailing space, again i'm sorry

Related

How to insert ManyToMany field in django

I want to insert a ManyToMany fields in my db using django.I select some customers using checkboxes.
This is my models.py :
class Campaign(models.Model):
title = models.CharField(max_length=255)
channel = models.CharField(max_length=255)
start_date = models.DateField()
end_date = models.DateField()
target_prospect = models.ManyToManyField(ProspectClient,related_name='campaigns_prospect')
target_partner = models.ManyToManyField(PartnerClient,related_name='campaigns_partners')
I try the code below in my views.py but didn't work :
def campaigns_page(request):
if request.user.is_authenticated:
if request.user.profile == 'D' or request.user.profile == 'E' or request.user.is_superuser:
campaigns = Campaign.objects.all()
prospects = ProspectClient.objects.all()
partners = PartnerClient.objects.exclude(id__in=PartnerClient.objects.values('id')).all()
context = {
'campaigns':campaigns,
'prospects':prospects,
'partners':partners
}
if request.method == 'POST':
title = request.POST['title']
channel = request.POST['channel']
start_date = request.POST['start_date']
end_date = request.POST['end_date']
descriptions = request.POST['goals'].split(",")
targets = request.POST['targets']
campaign = Campaign.objects.create(title=title,channel=channel,start_date=start_date,end_date=end_date)
for description in descriptions:
goal = Goal.objects.create(description=description)
goal.campaign.add(campaign)
for target in targets:
prospects.campaign.add(campaign)
partners.campaign.add(campaign)
return render(request,'CampaignManagement/campaigns_page.html',context)
return render(request, 'Login/logout.html')
If I delete the part of tergets it works.
But with this part it gives me This error : 'QuerySet' object has no attribute 'campaign'
How I can solve this ?
I see a couple of errors. Perhaps one or more are leading to the problem.
One
Try printing this:
partners = PartnerClient.objects.exclude(id__in=PartnerClient.objects.values('id')).all()
print(partners)
I suspect it will print None since you are excluding all id's in PartnerClient.objects.values('id'). On another note you don't need the all() since exclude() will return all the results you are looking for.
Two
In the line for target in targets: what exactly are you iterating through? targets = request.POST['targets'] is just giving you a string, so it would iterate through each letter. Perhaps you meant:
targets = request.POST['targets'].split(", ")
like you did for descriptions? Or perhaps you are getting a list of items from your form, in which case you can use:
targets = request.POST.getlist('targets')

Django : Best way to Query a M2M Field , and count occurences

class Edge(BaseInfo):
source = models.ForeignKey('Node', on_delete=models.CASCADE,related_name="is_source")
target = models.ForeignKey('Node', on_delete=models.CASCADE,related_name="is_target")
def __str__(self):
return '%s' % (self.label)
class Meta:
unique_together = ('source','target','label','notes')
class Node(BaseInfo):
item_type_list = [('profile','Profile'),
('page','Page'),
('group','Group'),
('post','Post'),
('phone','Phone'),
('website','Website'),
('email','Email'),
('varia','Varia')
]
item_type = models.CharField(max_length=200,choices=item_type_list,blank = True,null=True)
firstname = models.CharField(max_length=200,blank = True, null=True)
lastname = models.CharField(max_length=200,blank = True,null=True)
identified = models.BooleanField(blank=True,null=True,default=False)
username = models.CharField(max_length=200, blank=True, null=True)
uid = models.CharField(max_length=200,blank=True,null=True)
url = models.CharField(max_length=2000,blank=True,null=True)
edges = models.ManyToManyField('self', through='Edge',blank = True)
I have a Model Node (in this case a soc media profile - item_type) that has relations with other nodes (in this case a post). A profile can be the author of a post. An other profile can like or comment that post.
Question : what is the most efficient way to get all the distinct profiles that liked or commented on anothes profile's post + the count of these likes /comments.
print(Edge.objects.filter(Q(label="Liked")|Q(label="Commented"),q).values("source").annotate(c=Count('source')))
Gets me somewhere but i have the values then (id) and i want to pass the objects to my template rather then .get() all the profiles again...
Result :
Thanks in advance
I ended up with iterating over the queryset and adding the objects that i wanted in a dictionary , if the object was already in dictionary , i would count +1 and add the relation in a nested list.
This doesnt feel right but works for now.
posts = Edge.objects.filter(source = self,target__item_type='post',label='Author')
if posts:
q = Q()
for post in posts:
q = q | Q(target=post.target)
contributors = Edge.objects.filter(Q(label="Liked")|Q(label="Commented"),q)
if contributors:
for i in contributors:
if i.source.uid in results:
if i.label in results[i.source.uid]['relation']:
pass
else:
results[i.source.uid]["relation"].append(i.label)
if 'post' in results[i.source.uid]:
results[i.source.uid]['post'].append(i.target)
else:
results[i.source.uid]['post']=[i.target]
else:
results[i.source.uid] = {'profile' : i.source , 'relation':[i.label],'post':[i.target]}

How do I display Django data from a related model of a related model?

I am trying to display data from several models that are related together through a QuerySet. My ultimate goal is to display some information from the Site model, and some information from the Ppack model, based on a date range filter of the sw_delivery_date in the Site model.
Here are my models:
class Site(models.Model):
mnemonic = models.CharField(max_length = 5)
site_name = models.CharField(max_length = 100)
assigned_tech = models.ForeignKey('Person', on_delete=models.CASCADE, null = True, blank = True)
hw_handoff_date = models.DateField(null = True, blank = True)
sw_delivery_date = models.DateField(null = True, blank = True)
go_live_date = models.DateField(null = True, blank = True)
web_url = models.CharField(max_length = 100, null = True, blank = True)
idp_url = models.CharField(max_length = 100, null = True, blank = True)
def __str__(self):
return '(' + self.mnemonic + ') ' + self.site_name
class Ring(models.Model):
ring = models.IntegerField()
def __str__(self):
return "6." + str(self.ring)
class Ppack(models.Model):
ppack = models.IntegerField()
ring = models.ForeignKey('Ring', on_delete=models.CASCADE)
def __str__(self):
return str(self.ring) + " pp" + str(self.ppack)
class Code_Release(models.Model):
Inhouse = 'I'
Test = 'T'
Live = 'L'
Ring_Location_Choices = (
(Inhouse, 'Inhouse'),
(Test, 'Test'),
(Live, 'Live'),
)
site_id = models.ForeignKey('Site', on_delete=models.CASCADE)
type = models.CharField(max_length = 1, choices = Ring_Location_Choices, blank = True, null = True)
release = models.ForeignKey('Ppack', on_delete=models.CASCADE)
def __str__(self):
return "site:" + str(self.site_id) + ", " + self.type + " = " + str(self.release)
If I use the following,
today = datetime.date.today()
future = datetime.timedelta(days=60)
new_deliveries = Site.objects.select_related().filter(sw_delivery_date__range=[today, (today + future)])
I can get all of the objects in the Site model that meet my criteria, however, because there is no relation from Site to Code_Release (there's a one-to-many coming the other way), I can't get at the Code_Release data.
If I run a for loop, I can iterate through every Site returned from the above query, and select the data from the Code_Release model, which allows me to get the related data from the Ppack and Ring models.
site_itl = {}
itl = {}
for delivery in new_deliveries:
releases = Code_Release.objects.select_related().filter(site_id = delivery.id)
for rel in releases:
itl[rel.id] = rel.release
site_itl[delivery.id] = itl
But, that seems overly complex to me, with multiple database hits and possibly a difficult time parsing through that in the template.
Based on that, I was thinking that I needed to select from the Code_Release model. That relates back to both the Site model and the Ppack model (which relates to the Ring model). I've struggled to make the right query / access the data in this way that accomplishes what I want, but I feel this is the right way to go.
How would I best accomplish this?
You can use RelatedManager here. When you declare ForeignKey, Django allows you to access reverse relationship. To be specific, let's say that you have multiple code releases that are pointing to one specific site. You can access them all via site object by using <your_model_name_lowercase>_set attribute. So in your case:
site.code_release_set.all()
will return QuerySet of all code release objects that have ForeignKey to object site
You can access the Releases from a Site object. First, you can put a related_name to have a friendly name of the reverse relation between the models:
site_id = models.ForeignKey('Site', on_delete=models.CASCADE, related_name="releases")
and then, from a Site object you can make normal queries to Release model:
site.releases.all()
site.releases.filter(...)
...

How do I store a string in ArrayField? (Django and PostgreSQL)

I am unable to store a string in ArrayField. There are no exceptions thrown when I try to save something in it, but the array remains empty.
Here is some code from models.py :
# models.py
from django.db import models
import uuid
from django.contrib.auth.models import User
from django.contrib.postgres.fields import JSONField, ArrayField
# Create your models here.
class UserDetail(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
key = models.CharField(max_length=50, default=False, primary_key=True)
api_secret = models.CharField(max_length=50)
user_categories = ArrayField(models.CharField(max_length = 1000), default = list)
def __str__(self):
return self.key
class PreParentProduct(models.Model):
product_user = models.ForeignKey(UserDetail, default=False, on_delete=models.CASCADE)
product_url = models.URLField(max_length = 1000)
pre_product_title = models.CharField(max_length=600)
pre_product_description = models.CharField(max_length=2000)
pre_product_variants_data = JSONField(blank=True, null=True)
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
def __str__(self):
return self.pre_product_title
I try to save it this way:
catlist = ast.literal_eval(res.text)
for jsonitem in catlist:
key = jsonitem.get('name')
id = jsonitem.get("id")
dictionary = {}
dictionary['name'] = key
dictionary['id'] = id
tba = json.dumps(dictionary)
print("It works till here.")
print(type(tba))
usersearch[0].user_categories.append(tba)
print(usersearch[0].user_categories)
usersearch[0].save()
print(usersearch[0].user_categories)
The output I get is:
It works till here.
<class 'str'>
[]
It works till here.
<class 'str'>
[]
[]
Is this the correct way to store a string inside ArrayField?
I cannot store JSONField inside an ArrayField, so I had to convert it to a string.
How do I fix this?
Solution to the append problem.
You haven't demonstrated how your usersearch[0] I suspect it's something like this:
usersearch = UserDetail.objects.all()
If that is so you are making changes to a resultset, those things are immutable. Try this you will see that the id is unchanged too:
usersearch[0].id = 1000
print usersearch.id
But this works
usersearch = list(UserDetail.objects.all())
and so does
u = usersearch[0]
Solution to the real problem
user_categories = ArrayField(models.CharField(max_length = 1000), default = list)
This is wrong. ArrayFields shouldn't be used in this manner. You will soon find that you need to search through them and
Arrays are not sets; searching for specific array elements can be a
sign of database misdesign. Consider using a separate table with a row
for each item that would be an array element. This will be easier to
search, and is likely to scale better for a large number of elements
ref: https://www.postgresql.org/docs/9.5/static/arrays.html
You need to normalize your data. You need to have a category model and your UserDetail should be related to it through a foreign key.

Django: Two fields are unique but still fail UNIQUE constraint

I am writing some test to check the model I have for a basic blog app. The model requires that blog titles be unique. The following is the body of the test I have written to save two blog posts:
first_post.title = "First Post!"
first_post.body = "This is the body of the first post"
first_post.pub_date = datetime.date.today()
first_post.tags = all_tags[0]
first_post.slug = "first_post"
first_post.save()
second_post = Post()
second_post.title = "Second Post!"
self.assertNotEqual(first_post.title,second_post.title)
second_post.body = "This is the body of the Second post"
second_post.pub_date = datetime.date.today()
second_post.tags = all_tags[1]
second_post.slug = "second"
second_post.save()
Note the self.assertNotEqual(first_post.title, second_post.title). I added this because when I run the test I keep getting django.db.utils.IntegrityError: UNIQUE constraint failed: blog_post.title_text. When I did through the rest of the vomitext that is spit out with this it points to second_post.save(). However, the assertNotEqual always passes, if I change it to assertEqual it fails.
No matter what I put into the title value I get the same error. Why are these two Post objects considered to have the same title?
For reference, here is the blog model:
class Post(models.Model):
title_text = models.CharField(max_length = 200, unique = True)
pub_date = models.DateTimeField('date published')
post_tags = models.ManyToManyField('Tag')
post_body = models.TextField()
slug = models.SlugField(max_length = 50, unique = True)
The field in your model is named title_text, but in your test you use title. So the db value of title_text will be "" in both cases.
Change to this:
first_post.title_text = "First Post!"
first_post.body = "This is the body of the first post"
first_post.pub_date = datetime.date.today()
first_post.tags = all_tags[0]
first_post.slug = "first_post"
first_post.save()
second_post = Post()
second_post.title_text = "Second Post!"
self.assertNotEqual(first_post.title_text,second_post.title_text)
second_post.body = "This is the body of the Second post"
second_post.pub_date = datetime.date.today()
second_post.tags = all_tags[1]
second_post.slug = "second"
second_post.save()

Categories

Resources