Django ORM - updating specific instance with a user-ForeignKey - python

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

Related

Combine Data of Two Tables in single DropDown using Django

Problem: I have Two Tables Users and Users Group and in front end on
request of that particular page need to send all data from both the
table together, as there is specific drop down to show them both, and after done with the operation of that page data will get back in POST request (current models structures is given below), i am not getting how do i make connection in all these three tables so that it will get managed, Please let me know.
Model: User.py
class Users(AbstractBaseUser):
vendor_name = models.ForeignKey(Vendor, on_delete=models.CASCADE, default=None, null=True)
username = models.CharField(max_length=100, verbose_name="username", unique=True)
password = models.CharField(max_length=100)
created_by = models.DateField(verbose_name="created_by", auto_now_add=True)
USERNAME_FIELD = "username"
REQUIRED_FIELDS = ['password', 'hardware_id']
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_role_vendor = models.BooleanField(default=False)
is_role_customer = models.BooleanField(default=True)
def __str__(self):
return self.username
objects = UserManager()
Model: UserGroup.py
class UserGroup(models.Model):
vendor_id = models.ForeignKey(Vendor, on_delete=models.CASCADE, default=None, null=True)
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=100)
description = models.CharField(max_length=1000)
users = models.ManyToManyField(Users)
def __str__(self):
return self.name
Model: Rules.py
class Rules(models.Model):
vendor_id = models.ForeignKey(Vendor, on_delete=models.CASCADE, default=None, null=True)
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=100)
description = models.CharField(max_length=1000)
# Here i need to mention the code for field where i can store the combined value of User and UserGroup [selected from the dropdown].
def __str__(self):
return self.name
Need Solution:
How do i code in View to fetch the data of Two tables to send them for DropDown. {GET Request}
How will i store the values for the same together in Rules Table { As i said DropDown consist both values and can be selected all
together. }
The Structure of the Model {with the required Changes}.
there is no out of the box solution for that. I can advise to seprate this dropdown into two. First with UserGroup, second with User. You can fill user dropdown based on selected UserGroup with ajax or htmx -> htmx value-select
In your model Rules (should be Rule)
add fields:
user = models.ForeignKey(Users, on_delete=models.CASCADE)
group = models.ForeignKey(UserGroup, on_delete=models.CASCADE)
if there can be only one rule per Users(this should also be User)/UserGroup add unique_toigether to model Rules:
unique_together = ['user', 'group']
django docs unique_together

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.

Filtering data from joining table in Django by foreign key

I have model classes that look like:
class Wine(models.Model):
wine_id = models.IntegerField(blank=True, null=False, primary_key=True)
wine_name = models.TextField(blank=True, null=True)
wine_type = models.TextField(blank=True, null=True)
wine_year = models.IntegerField(blank=True, null=True)
wine_alcohol = models.FloatField(blank=True, null=True)
wine_country = models.TextField(blank=True, null=True)
wine_price = models.FloatField(blank=True, null=True)
class Meta:
managed = False
db_table = 'wine'
class Flavor(models.Model):
flavor_id = models.IntegerField(primary_key=False)
flavor_name = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'flavor'
and one joining table between these two:
class FlavorWine(models.Model):
flavor_wine_id = models.IntegerField(blank=True, null=False, primary_key=True)
flavor_group = models.TextField(blank=True, null=True)
flavor_count = models.IntegerField(blank=True, null=True)
wine_id = models.ForeignKey('Wine', on_delete=models.DO_NOTHING)
flavor_id = models.ForeignKey('Flavor', on_delete=models.DO_NOTHING)
class Meta:
managed = False
db_table = 'flavor_wine'
Now, whenever I try to retrieve the data I get errors.
I tried exampled used in: Django Filter by Foreign Key and Django: join two tables, but to no success.
I tried:
wines = Wine.objects.filter(wine_id=wine_id)
wine_flavor = FlavorWine.objects.filter(wine_id__in=wines.values('wine_id'))
return HttpResponse(serializers.serialize('json', wine_flavor, fields=('wine_id', 'flavor_group', 'flavor_count', 'flavor_id')))
and
wine_flavor = serializers.serialize('json', FlavorWine.objects.filter(wine_id_id__gt=wine_id), fields=('wine_id', 'flavor_group', 'flavor_count', 'flavor_id'))
and
wine_flavor = serializers.serialize('json', FlavorWine.objects.filter(wine_id__flavorwine__exact=wine_id), fields=('wine_id', 'flavor_group', 'flavor_count', 'flavor_id'))
And different combinations that were offerred, but none of them work, either it fails when joining tables or it cannot find the required field.
I always get the hint:
HINT: Perhaps you meant to reference the column "flavor_wine.wine_id".
I mean, that's the exact column I'm trying to reference, but I cannot find the proper way of doing so.
Try this article out, https://www.django-antipatterns.com/antipattern/foreign-key-with-id-suffix.html and see if it fixes your issue.
The main reason why it is a problem is because the .other_model itself does not store the id. Indeed, Django makes an implicit twin-field
with an _id suffix that stores the primary key
To filter from ForeignKey you can simply pass that model's instance
In you first method:
wine_flavor = FlavorWine.objects.filter(wine_id__in=wines.values('wine_id'))
if wine_id is an instance of Wine model then you can simply write
wine_flavor = FlavorWine.objects.filter(wine_id=wine_id)
and if wine_id is a list of ids from Wine model then you can just right the following:
wine_flavor = FlavorWine.objects.filter(wine_id__id__in=wine_id)
Let me explain what above line does,
suppose wine_id = ['1', '2', '3',....]
where '1' represent id from Wine model
then filter those from FlavorWine(FlavorWine.objects.filter)
Where id from Wine model(wine_id__id) is in list(wine_id)
(if you need any more help then comment below, I can update my answer accordingly)
So in order to filter between related models by foreignkey, it's pretty straight forward, by using the prefetct_ralated function which takes the argument of the related name of the models. As in my example below. Here is a link to read more to help you understand querying with prefetch related. https://docs.djangoproject.com/en/4.0/ref/models/querysets/#prefetch-related
wines = Wine.objects.prefetch_related('flavorwine_set').filter(wine_id=wine_id)
wine_flavor = FlavorWine.objects.prefetch_related('flavorwine_set').filter(wine_id__in=wines.values('wine_id'))

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.

Why Django DoesNotExist Erase other objects?

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

Categories

Resources