Why Django DoesNotExist Erase other objects? - python

I'm facing an issue with Django and specially the use of DoesNotExist.
In my script, I'm trying to check if 'Scraper' object exist or not, if it doesn't the object is created.
For checking if it exist or not i'm using a 'try catch Model.DoesNotExists', .get() with two parameters the first one is an IDs and the last the User object.
The issue is rising when two Scraper object has a same ids but different user's, the first one 'Scraper' is created and the second erase the first one.
try:
a = Scraper.objects.get(id_lbc=ids, user=user)
except Scraper.DoesNotExist:
a = Scraper(
id_lbc=ids,
lbc_name=title,
lbc_url=url,
lbc_image=image,
lbc_price=price,
lbc_date=date,
lbc_city=city,
lbc_price_meter=price_meter,
user=user,
token_url=token,
customer=customer,
has_phone=phone,
)
a.save()
Model
class Scraper(models.Model):
id_lbc = models.IntegerField(primary_key=True)
lbc_name = models.CharField(max_length=300)
lbc_url = models.URLField(max_length=300)
lbc_image = models.URLField(
max_length=300, default='http://****/****/nothing.jpg')
lbc_price = models.IntegerField(blank=True, null=True)
lbc_price_meter = models.IntegerField(blank=True, null=True)
lbc_city = models.CharField(max_length=300)
lbc_date = models.DateTimeField(auto_now=False)
user = models.ForeignKey(User, on_delete=models.CASCADE)
token_url = models.ForeignKey(
Url_lbc, to_field='token', on_delete=models.CASCADE)
is_emailed = models.BooleanField(default=False)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
has_phone = models.BooleanField(default=False)
def __str__(self):
return self.lbc_name
What I want is create two Scraper object with same 'ids' but different User's
for example
Scraper N°1
ids = 1234567
user = Nico(object)
Scraper N°2
ids = 1234567
user = Paul(object)
I thought with the User object given, the Django query can see the difference between two Scraper object but I misunderstood something...maybe the PK in my model ?
Thanks for the help

You can't do what you want with your current model design. This is because id_lbc is set as a primary key. This means it has to be unique, you can't have two istances sharing that value. What you could do though is:
class Scraper(models.Model):
id_lbc = models.IntegerField()
user = models.ForeignKey(User, on_delete=models.CASCADE)
...
class Meta:
unique_together = [('user', 'id_lbc')]
This will make it such that id_lbc can have duplicates within the table as long as each value has a different user value.
Then you should also use Scraper.objects.get_or_create:
a, created = Scraper.objects.get_or_create(id_lbc=ids, user=user, defaults={
'lbc_name': title,
'lbc_url': url,
'lbc_image': image,
'lbc_price': price,
'lbc_date': date,
'lbc_city': city,
'lbc_price_meter': price_meter,
'token_url': token,
'customer': customer,
'has_phone': phone,
})

Do not use the Scraper directly to create the object, call instead Scraper.objects.create(...)

Related

Populating sql table with foreign keys using django

So im having some trouble inserting data into my sql database when using django. Setting up the tables aswell as populating them trough the admin page works perfectly fine but i have a scraper function that runts every 24h thats supposed to insert data.
from datetime import datetime
from .faceScraper import faceScraper as fc
def Dbpopulator():
from ..models import Event
[title_list, time_list, location_list, nation_list] = fc()
print("scraped")
for i in range(len(title_list)):
e = Event()
e.title = title_list[i]
e.starttime = time_list[i][0]
e.endtime = time_list[i][1]
e.location = location_list[i]
instance = e.save(commit=False)
instance.nation = nation_list[i]
instance.save()
The problem arises when im trying to set the nation which is a foreign key from the models file below.
from django.db import models
# Create your models here.
class Nation(models.Model):
name = models.CharField(max_length=80, primary_key=True)
description = models.TextField()
Facebook = models.CharField(max_length=80, null=True, blank=True)
def __str__(self):
return self.name
class Event(models.Model):
title = models.CharField(max_length=80, unique=True)
starttime = models.DateTimeField(null=True, blank=True)
endtime = models.DateTimeField(null=True, blank=True)
nation = models.ForeignKey(
Nation, on_delete=models.CASCADE, default=None, null=True, blank=True)
location = models.CharField(max_length=80, null=True, blank=True)
def __str__(self):
return self.title
I have tried many different ways primarily just setting the e and saving like normal, aka cutting out all the instance and commit=false.
e.nation = nation_list[i]
e.save()
But it just wont work, i am also very certain that the database is already populated with nations which contains names that corresponds to what im trying to insert as i can see thoose on the admin page.
All help apreciated!
You need to get the Nation instance first. If nation-list contains the names of the nations, you can get them like this :
e = Event()
...
e.nation = Nation.objects.get(name=nation_list[i])
e.save()
If you're not a 100% sure that provided name will match a Nation, you can use .filter(...).first() rather than .get(...) to avoid any crash.

Get max value from a set of rows

This question is in relation to project 2 of the cs50 course which can be found here
I have looked at the following documentation:
Django queryset API ref
Django making queries
Plus, I have also taken a look at the aggregate and annotate things.
I've created the table in the template file, which is pretty straight forward I think. The missing column is what I'm trying to fill. Image below
These are the models that I have created
class User(AbstractUser):
pass
class Category(models.Model):
category = models.CharField(max_length=50)
def __str__(self):
return self.category
class Listing(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
description = models.TextField()
initial_bid = models.IntegerField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
date_created = models.DateField(auto_now=True)
def __str__(self):
return self.title
class Bid(models.Model):
whoDidBid = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
list_item = models.ForeignKey(Listing, default=0, on_delete=models.CASCADE)
bid = models.IntegerField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
date = models.DateTimeField(auto_now=True)
def __str__(self):
return_string = '{0.whoDidBid} {0.list_item} {0.bid}'
return return_string.format(self)
This is the closest I could come to after a very long time. But the result I get is just the number 2. Ref image below
Listing.objects.filter(title='Cabinet').aggregate(Max('bid'))
Where 'Cabinet' is a Listing object that I have created. And placed two bids on them.
So the question is, how do I get the Maximum bid value(i.e. 110 for this case) for a particular listing? Using the orm. I think if I used a raw sql query, I could build a dict, send it to the template with the queryset. Then while looping through the queryset, get the value for the key, where the key is the name of the listing or something along those lines. Nah, I would like to know how to do this through the ORM please.
Here's answer #1
Bid.objects.filter(list_item__title='Cabinet').prefetch_related('list_item').aggregate(Max('bid'))
What happens when you try this (sorry, I don't have any objects like this to test on):
Bid.objects.values(list_item__title).prefetch_related('list_item').annotate(Max('bid'))

How to iterate through all foreign keys pointed at an object in Django without using _set notation?

I am fairly new to Django, but I am working on an application that will follow a CPQ flow or Configure, Price, Quote. The user should select the product they would like to configure as well as the options to go with it. Once selected, the program should query an external pricing database to calculate price. From there the program should output the pricing & text data onto a PDF quote. I was able to get the application working using the specific product inheriting from a base product class. The issue is now that I've created a second product child class, I cannot use a singular "related_name". I've omitted the lists associated with the drop down fields to help with readability, but I've posted my models.py file below.
Is there a way I can iterate through Product objects that are pointing to a Quote object with a foreign key? A lot of answers I've found on SO relating to this were able to be solved either by specifying the "_set" or "related_name". I've seen other answers use the select_related() method, however, I can't seem to get the query right as the program won't know which set it needs to look at. A quote could have any mix of product instances tied to it, so am unsure how to handle that query. Again, I have been using django under 6 months, so I am a bit green. I am not sure if I am just not fundamentally understanding the big picture here. I thought about instead of using inheritance, to make Product a standalone class and to save the Compact or WRC info to it so I could just use one "related_name", but also thought that would just create another nested layer that would still fail.
Any help would be very appreciated! I've definitely hit the wall.
models.py
class Quote(models.Model):
project_name = models.CharField(max_length=256,blank=True)
customer_first_name = models.CharField(max_length=256,blank=True)
customer_last_name = models.CharField(max_length=256,blank=True)
company_name = models.CharField(max_length=256, blank=True)
address1 = models.CharField(max_length=256, blank=True, help_text ="Address")
address2 = models.CharField(max_length=256, blank=True)
city = models.CharField(max_length=256, blank=True, default="")
state = models.CharField(max_length=256, blank=True, default="")
zip_code = models.CharField(max_length=256, blank=True, default="")
country = models.CharField(max_length=256, blank=True, default="")
phone = PhoneField(blank=True)
email = models.EmailField(max_length=254,blank=True)
grand_total = models.FloatField(default=0)
create_date = models.DateTimeField(default = timezone.now)
class Product(models.Model):
class Meta:
abstract = True
price = models.FloatField(default=0)
total_price = models.FloatField(default=0)
quantity = models.IntegerField()
quote = models.ForeignKey('quote.Quote', on_delete=models.CASCADE)
quantity = models.IntegerField()
class Compact(Product):
base_size = models.CharField(choices=size, max_length = 256)
filter = models.CharField(choices=filter_list, max_length = 256)
product_name = models.CharField(max_length=256,default="Compact")
class WRC(Product):
base_size = models.CharField(choices=size, max_length = 256)
construction = models.CharField(choices=construction_list, max_length = 256)
outlet = models.CharField(choices=outlet_list, max_length = 256)
product_name = models.CharField(max_length=256,default="WRC")
I was able to figure out my issue, but wanted to answer in case someone came across a similar problem as myself. I was able to get get all product objects attached to a quote instance dynamically by modifying the get_context_data() method of my QuoteDetailView. I also needed to use the django library NestedObjects from django.contrib.admin.utils to grab all related objects to the quote instance. I also added a timestamp field to the Product class to be able to sort them. QuoteDetailView copied below.
class QuoteDetailView(FormMixin,DetailView):
model = Quote
form_class = ProductSelectForm
def get_context_data(self, **kwargs):
### collects related objects from quote
collector = NestedObjects(using=DEFAULT_DB_ALIAS)
collector.collect([kwargs['object']])
### slice off first element which is the quote itself
related_objects = collector.nested()
related_objects = related_objects[1:]
### get context data for qoute object
context = super().get_context_data(**kwargs)
context['now'] = timezone.now()
### if number of list items is above 0, then add them to the context
### and sort by timestamp
if len(related_objects) != 0:
context['items'] = sorted(related_objects[0], key=lambda x: x.timestamp)
return context

Field 'id' expected a number but got <Listing: Ps4 Pro>

It's my first time creating a Django website with models, and in my first attempt to insert data into my table I'm getting this error.
My models are as follows:
class User(AbstractUser):
pass
#https://docs.djangoproject.com/en/3.1/topics/auth/default/
class Listing(models.Model):
listingID = models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="listID")
created_by = models.ForeignKey(User, on_delete=models.SET_NULL, related_name="myListing", null=True)
watchers = models.ManyToManyField(User, blank=True, related_name="watchlist")
title = models.CharField(max_length=30)
description = models.TextField()
creation_date = models.DateField(auto_now=True)
img_url = models.URLField()
active = models.BooleanField(default=True)
def __str__(self):
return f"{self.title}"
class Bid(models.Model):
listing = models.ForeignKey(Listing, on_delete=models.SET_NULL, related_name="bidsMadeOnMe", null=True, blank=True)
user = models.ForeignKey(User, on_delete=models.SET_NULL, related_name="myBids", null=True)
price = models.FloatField()
creation_date = models.DateField(auto_now=True, null=True)
def __str__(self):
return f"Bid={self.price}"
and the view that handles the form submission is this one:
#login_required
def create_listing(request):
if request.method == "POST":
user = User.objects.get(username=request.user.username)
l = Listing(created_by=user,
title=request.POST["title"],
description=request.POST["desc"],
# https://stackoverflow.com/questions/12176585/handling-dates-over-request-get
creation_date=models.DateTimeField(auto_now_add=True),
img_url=request.POST["image_url"]
)
l.save()
b = Bid(l,
user,
request.POST["initial_bid"],
models.DateTimeField(auto_now_add=True)
)
b.save()
return render(request, "auctions/index.html")
I know the problem is the way I'm adding the data but I can't fix it. Can someone give me some light?
Your problem (well, several actually) is this:
b = Bid(l, user, request.POST["initial_bid"], models.DateTimeField(auto_now_add=True))
You're constructing a model instance by positional arguments instead of keyword arguments. This can be done, but then the invisible "id" column that has been added to the Bid model, is the first argument.
In Django we never construct models like that, but always use keyword arguments, so we're not depending on field order:
b = Bid(listing=l, user=user, ...))
Once you're solved that, your next problem is the date field.
Don't assign fields to model instances. Fields are class declarations, they don't belong on instances. Fields describe on a class (= a Model), what kind data to expect. On the instance, you assign that data.
In this case, your definition for the field is wrong on the model and on the instance you shouldn't even assign it - it will be automatically filled.
Overall, it feels like you haven't gone through Django's tutorial or did not fully understand the concepts. I suggest you go through it.

Django ORM - updating specific instance with a user-ForeignKey

I have the following model setup:
class Model1(models.Model):
val1_1 = models.CharField(max_length=25, blank=True)
val1_2 = models.CharField(max_length=25, blank=True)
user = models.ForeignKey('users.User', on_delete=models.PROTECT, related_name='model1')
class Model2(models.Model):
val2_1 = models.BinaryField()
model1_link = models.ForeignKey(Case, on_delete=models.CASCADE, related_name='model2')
class Model3(models.Model):
id = models.BigAutoField(primary_key=True)
model2_link = models.ForeignKey(Model2, on_delete=models.CASCADE, related_name='model3')
val3_1 = models.CharField(max_length=50)
class Model4(models.Model):
id = models.BigAutoField(primary_key=True)
model3_link = models.ForeignKey(Model3, on_delete=models.CASCADE, related_name='model4', null=True, default=None)
pred = models.CharField(max_length=50)
# These fields are NOT FILLED IN during creation of an instance, and are instead updated later on with a separate query
disputed_on = models.DateTimeField(blank=True, null=True)
suggested_by = models.ForeignKey('users.User', on_delete=models.PROTECT, related_name='submitted_disputes', blank=True, null=True)
At times, I will want to access Model4's specific instance, to actually fill in a value in fields disputed_on & suggested_by, by traversing all the way from Model1. I do that currently as such:
query = Model1.objects.filter(id=some_chosen_id).get().model2.last().model3.filter(val3_1=some_chosen_value).get().model4.last()
The output of that query is a single model instance, not a QuerySet.
Next, I calculate new values I want to insert:
dispute_date = datetime.now(tz.tzutc())
if request.user.is_authenticated:
disputer = request.user
else:
# Assume admin (first entry)
disputer = User.objects.get(pk=1)
And I save new values by doing the following:
query.disputed_on = dispute_date
query.suggested_by = disputer
query.save()
Now, the strangest thing happens - my postgres DB gives me an error stating, the following:
postgres_1 | 2020-03-30 11:45:31.700 AEDT [4169] ERROR: duplicate key value violates unique constraint "users_user_username_key"
Now, the way I read it, calling prediction.save() also results in DB trying to update table of users. But I do not call for that anywhere in the code!
Any ideas why is that happening?
That problem seems to be gone when I modify my query to stay as a QuerySet, and use .update() method instead...

Categories

Resources