Django: Two fields are unique but still fail UNIQUE constraint - python

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()

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(...)
...

Iterating with Django ORM through large datasets is slow

I'm using Django ORM to get data out of a database with a few million items. However, computation takes a while (40 minutes+), and I'm not sure how to pin point where the issue is located.
Models I've used:
class user_chartConfigurationData(models.Model):
username_chartNum = models.ForeignKey(user_chartConfiguration, related_name='user_chartConfigurationData_username_chartNum')
openedConfig = models.ForeignKey(user_chartConfigurationChartID, related_name='user_chartConfigurationData_user_chartConfigurationChartID')
username_selects = models.CharField(max_length=200)
blockName = models.CharField(max_length=200)
stage = models.CharField(max_length=200)
variable = models.CharField(max_length=200)
condition = models.CharField(max_length=200)
value = models.CharField(max_length=200)
type = models.CharField(max_length=200)
order = models.IntegerField()
def __unicode__(self):
return str(self.username_chartNum)
order = models.IntegerField()
class data_parsed(models.Model):
setid = models.ForeignKey(sett, related_name='data_parsed_setid', primary_key=True)
setid_hash = models.CharField(max_length=100, db_index = True)
block = models.CharField(max_length=2000, db_index = True)
username = models.CharField(max_length=2000, db_index = True)
time = models.IntegerField(db_index = True)
time_string = models.CharField(max_length=200, db_index = True)
def __unicode__(self):
return str(self.setid)
class unique_variables(models.Model):
setid = models.ForeignKey(sett, related_name='unique_variables_setid')
setid_hash = models.CharField(max_length=100, db_index = True)
block = models.CharField(max_length=200, db_index = True)
stage = models.CharField(max_length=200, db_index = True)
variable = models.CharField(max_length=200, db_index = True)
value = models.CharField(max_length=2000, db_index = True)
class Meta:
unique_together = (("setid", "block", "variable", "stage", "value"),)
The code I'm running is looping through data_parsed, with relevant data that matches between user_chartConfigurationData and unique_variables.
#After we get the tab, we will get the configuration data from the config button. We will need the tab ID, which is chartNum, and the actual chart
#That is opened, which is the chartID.
chartIDKey = user_chartConfigurationChartID.objects.get(chartID = chartID)
for i in user_chartConfigurationData.objects.filter(username_chartNum = chartNum, openedConfig = chartIDKey).order_by('order').iterator():
iterator = data_parsed.objects.all().iterator()
#We will loop through parsed objects, and at the same time using the setid (unique for all blocks), which contains multiple
#variables. Using the condition, we can set the variable gte (greater than equal), or lte (less than equal), so that the condition match
#the setid for the data_parsed object, and variable condition
for contents in iterator:
#These are two flags, found is when we already have an entry inside a dictionary that already
#matches the same setid. Meaning they are the same blocks. For example FlowBranch and FlowPure can belong
#to the same block. Hence when we find an entry that matches the same id, we will put it in the same dictionary.
#Added is used when the current item does not map to a previous setid entry in the dictionary. Then we will need
#to add this new entry to the array of dictionary (set_of_pk_values). Otherwise, we will be adding a lot
#of entries that doesn't have any values for variables (because the value was added to another entry inside a dictionary)
found = False
added = False
storeItem = {}
#Initial information for the row
storeItem['block'] = contents.block
storeItem['username'] = contents.username
storeItem['setid'] = contents.setid
storeItem['setid_hash'] = contents.setid_hash
if (i.variable != ""):
for findPrevious in set_of_pk_values:
if(str(contents.setid) == str(findPrevious['setid'])):
try:
items = unique_variables.objects.get(setid = contents.setid, variable = i.variable)
findPrevious[variableName] = items.value
found = True
break
except:
pass
if(found == False):
try:
items = unique_variables.objects.get(setid = contents.setid, variable = i.variable)
storeItem[variableName] = items.value
added = True
except:
pass
if(found == False and added == True):
storeItem['time_string'] = contents.time_string
set_of_pk_values.append(storeItem)
I've tried to use select_related() or prefetch_related(), since it needs to go to unique_variables object and get some data, however, it still takes a long time.
Is there a better way to approach this problem?
Definitely, have a look at django_debug_toolbar. It will tell you how many queries you execute, and how long they last. Can't really live without this package when I have to optimize something =).
PS: Execution will be even slower.
edit: You may also want to enable db_index for the fields you use to filter with or index_together for more than one field. Ofc, measure the times between your changes so you make sure which option is better.

Save data to ManyToManyField error in django?

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

Categories

Resources