Multiple field foreign key in django - python

I have two models in django with definitions below.
CreativeStatus model :
class RtbdCreativeStatus(models.Model):
creative_id = models.CharField(max_length=500, primary_key=True)
advertiser_id = models.CharField(max_length=100, primary_key=True)
exposure_level = models.CharField(max_length=125)
modified_on = models.DateTimeField()
modified_by = models.CharField(max_length=100)
class RtbdCreative(models.Model):
id = models.AutoField(primary_key=True)
advertiser_id = models.ForeignKey(RtbdCreativeStatus, on_delete=models.CASCADE)
creative_id = models.ForeignKey(RtbdCreativeStatus, on_delete=models.CASCADE)
country_id = models.IntegerField()
adm = models.CharField(max_length=255, null=True, blank=True)
sample_url = models.CharField(max_length=500)
landing_page = models.CharField(max_length=500, null=True, blank=True)
html = models.CharField(max_length=500)
creative_attributes = models.CommaSeparatedIntegerField(max_length=150, null=True, blank=True)
advertiser_domains = models.CharField(max_length=500)
description = models.CharField(max_length=500, null=True, blank=True)
created_on = models.DateTimeField(auto_now=True, auto_now_add=True)
creative_type = models.CharField(max_length=50, null=True, blank=True)
demand_source_type_id = models.IntegerField()
revalidate = models.BooleanField(default=False)
(creative_id, advertiser_id ) combination is unique in my CreativeStatus table . I want that combination to be my foreign key for Creative table. I tried adding it but i get this error .
1)How do i achieve this join with two key combination as my foreign key .
2)What should be my query to fetch all the creatives with their status from CreativeStatus table .
UPDATE 1
on reading the answers below , i updated my model as mentioned below :
class RtbdCreative(models.Model):
id = models.AutoField(primary_key=True)
advertiser_id = models.ForeignKey(RtbdCreativeStatus, to_field='advertiser_id', related_name='advertiser', db_column='advertiser_id', on_delete=models.CASCADE)
creative_id = models.ForeignKey(RtbdCreativeStatus, to_field='creative_id', related_name='creative', db_column='creative_id', on_delete=models.CASCADE)
country_id = models.IntegerField()
adm = models.CharField(max_length=255, null=True, blank=True)
sample_url = models.CharField(max_length=500)
landing_page = models.CharField(max_length=500, null=True, blank=True)
html = models.CharField(max_length=500)
creative_attributes = models.CommaSeparatedIntegerField(max_length=150, null=True, blank=True)
advertiser_domains = models.CharField(max_length=500)
description = models.CharField(max_length=500, null=True, blank=True)
created_on = models.DateTimeField(auto_now=True, auto_now_add=True)
creative_type = models.CharField(max_length=50, null=True, blank=True)
demand_source_type_id = models.IntegerField()
revalidate = models.BooleanField(default=False)
Now i am getting this error . I have combination of advertiser_id , craetive_id as unique . But django expects both to be unique. What can i do to make it work ?

As mentioned in ERRROS, you need to add related_name as argument, when you want to add more than one foreign key for same Model.
class Creative(models.Model):
id = models.AutoField(primary_key=True)
advertiser_id = models.ForeignKey(RtbdCreativeStatus,
related_name="Advertiser", on_delete=models.CASCADE)
creative_id = models.ForeignKey(RtbdCreativeStatus,
related_name="Creative",
on_delete=models.CASCADE)
country_id = models.IntegerField()
adm = models.CharField(max_length=255, null=True, blank=True)
sample_url = models.CharField(max_length=500)
landing_page = models.CharField(max_length=500, null=True, blank=True)
html = models.CharField(max_length=500)
creative_attributes = models.CommaSeparatedIntegerField(
max_length=150, null=True, blank=True)
advertiser_domains = models.CharField(max_length=500)
description = models.CharField(max_length=500, null=True, blank=True)
created_on = models.DateTimeField(auto_now=True, auto_now_add=True)
creative_type = models.CharField(max_length=50, null=True, blank=True)
demand_source_type_id = models.IntegerField()
revalidate = models.BooleanField(default=False)

I just saw a parameter as to_fields for models.ForeignObject, superclass of models.ForeignKey. It might be used in this case for defining foreign key for composite primary key or unique keys.
advertiser_creative_id = models.ForeignObject(RtbdCreativeStatus, to_fields=['advertiser_id', 'creative_id'], related_name='abc', on_delete=models.CASCADE)
There is a from_fields parameter as well. It can be used to map the fields with to_fields.
Refer https://docs.djangoproject.com/en/2.2/_modules/django/db/models/fields/related/

When you add multiple ForeignKeys towards same table, you should override the related_name option of your fields, so that the fields could be distinguished easily.
You could implement a custom validation for checking uniqueness of the creative_id and advertiser_id,
class Creative(models.Model):
advertiser_id = models.ForeignKey(CreativeStatus,
related_name="advertisers")
creative_id = models.ForeignKey(CreativeStatus,
related_name="creatives")
def clean(self):
data = self.cleaned_data
if not data['advertiser_id'] == data['creative_id']:
raise ValidationError("Unique Constraint failed {}, {}".format(self.advertiser_id, self.creative_id))
return data
You could query your creatives from CreativeStatus using the related name.
creative_status_obj = CreativeStatus.objects.get(pk=some_pk)#or any query.
#All creatives of the given object can be queried using reverse relation.
creatives = creative_status_obj.creatives.all()

Related

Django Admin shows multiple instances for same primary key

I am struggling with a weird issue in Django (4.0.7) where multiple instances for the same primary key are shown in Django Admin, as well as when executing queries. I have displayed the primary keys to make clear that they are identical:
The two classes involved are Collection and Card, where every card has a foreign key to a collection.
class Collection(models.Model):
FREQUENCY_CHOICES = [('never', 'Never'), ('less', 'Less'), ('normal', 'Normal'), ('more', 'More')]
title = models.CharField(max_length=200)
author = models.CharField(max_length=200, blank=True, null=True)
user = models.ForeignKey(User, related_name='collections', on_delete=models.CASCADE)
type = models.CharField(max_length=10, choices=(('books', 'Books'), ('tweets', 'Tweets'), ('articles', 'Articles'), ('podcasts', 'Podcasts')))
custom_id = models.CharField(max_length=200, blank=True, null=True) # e.g. raindropref, amazon book id, etc.
url = models.URLField(blank=True, null=True)
tags = TaggableManager(blank=True)
connection = models.ForeignKey(Connection, related_name='collections', null=True, blank=True, on_delete=models.SET_NULL)
frequency = models.CharField(max_length=10, choices=FREQUENCY_CHOICES, default='normal')
class Card(models.Model):
text = models.TextField()
collection = models.ForeignKey(Collection, related_name='cards', on_delete=models.CASCADE, blank=True)
custom_id = models.CharField(max_length=200, null=True, blank=True)
author = models.CharField(max_length=200, null=True, blank=True)
url = models.URLField(blank=True, null=True)
created_at = models.DateTimeField(default=timezone.now)
favorite = models.BooleanField(default=False)
tags = TaggableManager(blank=True)
notes = models.TextField(blank=True, null=True)
location = models.IntegerField(blank=True, null=True)
I cannot fathom where the issue might be. I have already set up the databse from scratch, with no success.
Here is an example database query:
for c in Collection.objects.all():
print(c.pk, c.id)
12 12
12 12
12 12
13 13

Django Rest API: Serializer won't show Foreign Key Field values

I'm trying to list the values of FacilityAddressSerializer within the FacilitySearchSerializer. This is what i tried. I get all the values of the FacilitySearchSerializer but the values of the FacilityAddressSerializer are showing as Null:
serializers.py
class FacilityAddressSerializer(serializers.ModelSerializer):
class Meta:
model = FacilityAddress
fields = (
"id",
"PrimaryAddress",
"SecondaryAddress",
"City",
"RegionOrState",
"PostalCode",
"Geolocation",
"AddressInfo"
)
class FacilitySearchSerializer(serializers.ModelSerializer):
AddressInfo = FacilityAddressSerializer(source="fa")
class Meta:
model = Facility
fields = (
"id",
"Name",
"AddressInfo",
"ListingVerified",
"mainimage",
"AdministratorCell",
"Capacity",
"PriceRangeMin",
"PriceRangeMax",
)
read_only_fields = ("id", "Name", "ListingVerified", "mainimage", "AdministratorCell", "Capacity", "FeaturedVideo", "PriceRangeMin", "PriceRangeMax")
models.py
class Facility(models.Model):
Name = models.CharField(max_length=150, null=True, blank=False)
mainimage = models.ImageField(null=True, blank=True)
Capacity = models.IntegerField(null=True, blank=True)
TelephoneNumber = models.CharField(max_length=30, null=True, blank=True)
AdministratorCell = PhoneNumberField(null=True, blank=True)
PriceRangeMin = models.IntegerField(null=True, blank=True)
PriceRangeMax = models.IntegerField(null=True, blank=True)
class FacilityAddress(models.Model):
PrimaryAddress = models.CharField(max_length=150, null=True, blank=True)
SecondaryAddress = models.CharField(max_length=150, null=True, blank=True)
City = models.CharField(max_length=150, null=True, blank=True)
RegionOrState = models.CharField(max_length=50, null=True, blank=True)
PostalCode = models.CharField(max_length=30, null=True, blank=True)
Geolocation = models.CharField(max_length=30, null=True, blank=True)
AddressInfo = models.ForeignKey(Facility, null=True, blank=True, on_delete=models.CASCADE, related_name='fa')
It works after i added (many=True) next to the source=fa. I thought i didn't need that since i'm using foreign key fields and not manytomany fields but i guess i was wrong.

Referencing foreign key in Django Python

How to assign a product in ProductInStore to an instance of a Product already in store? Basically i have a scrapper and im looping through all the products and i need to first create the Product instance, and then A ProductInStore instance which is connected to Product via foreignKey. And when i try to put in ProductInStore(product=id) thats how i wanted to reference that Product, i get an error ValueError: Cannot assign "11393": "ProductInStore.product" must be a "Product" instance.
Do you have any idea how to reference it?
class Product(models.Model):
name = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique=True, null=False, editable=False)
created_at = models.DateTimeField(editable=False, default=timezone.now)
updated_at = models.DateTimeField(default=timezone.now)
product_category = models.ManyToManyField(EcommerceProductCategory)
description = RichTextField(max_length=2000, null=True, blank=True)
product_producer = models.ForeignKey('ProductProducer', on_delete=models.CASCADE)
creator = models.ForeignKey('users.CustomUser', on_delete=models.CASCADE, null=True, blank=True, related_name='product_creator')
points_from_reviews = models.DecimalField(max_digits=6, decimal_places=2, default=0, help_text='Średnia ocena produktu')
unique_id = models.CharField(max_length=256, unique=True)
type_of_unique_id = models.CharField(max_length=64)
class ProductInStore(models.Model):
product = models.ForeignKey('Product', on_delete=models.CASCADE)
store = models.ForeignKey('Store', on_delete=models.CASCADE)
price = models.DecimalField(max_digits=6, decimal_places=2, default=0, help_text='Cena w sklepie')
currency = models.CharField(max_length=4)
url = models.CharField(max_length=200)
created_at = models.DateTimeField(editable=False, default=timezone.now)
updated_at = models.DateTimeField(default=timezone.now)
# ADD TO DATABASE
try:
db_product = Product.objects.create(name=name, slug=slug, product_producer_id=3, unique_id=id,
description=description)
db_product.product_category.set(parsed_categories)
except:
print('product already in database')
ProductInStore.objects.create(product=,store=1,price=price,currency='PLN',url=url)
You can simply do this:
ProductInStore.objects.create(product_id=id, store=1, price=price, currency='PLN', url=url)
You don't need to pass the object if you have the ID of it, simply append _id to the field name and you can reference the foreign key that way.

OneToOne relationship not linking

I have these models:
class Article(TimestampedModel):
title = models.CharField(max_length=255)
slug = models.SlugField(db_index=True, max_length=255, unique=True, null=True, blank=True)
introduction = models.TextField(null=True, blank=True)
content = HTMLField('Content')
image = models.ImageField(null=True, blank=True, upload_to='articles')
parent = models.ForeignKey(
'core.Article', on_delete=models.CASCADE, related_name='sectionparent', null=True, blank=True
)
authors = models.ManyToManyField(People, blank=True)
active = models.BooleanField(default=True)
SECTIONS = (
('about', 'About'),
('newsevents', 'News and Events'),
)
section = models.CharField(max_length=20, choices=SECTIONS, default='about')
site = models.ForeignKey(Site, on_delete=models.CASCADE)
objects = models.Manager()
on_site = CurrentSiteManager()
class Event(models.Model):
article = models.OneToOneField(
Article,
on_delete=models.CASCADE,
related_name='event',
),
EVENT_TYPE = (
('match', 'Match'),
('other', 'Other'),
)
start = models.DateTimeField(null=True, blank=True);
end = models.DateTimeField(null=True, blank=True);
type = models.CharField(max_length=20, choices=EVENT_TYPE)
location = models.CharField(max_length=255, null=True, blank=True)
url = models.CharField(max_length=255, null=True, blank=True)
However, when I look at the generated events table in my database, there is no foreign key (I would have expected on). Furthermore, I have a record in Article:
article = Article.objects.find(1)
And then when I try to access the event:
event = article.event
I will get this error:
'Article' object has no attribute 'event'

Access A Specific Item In DB in my for statement

I have a somewhat odd scenario. I am using a read only database that I have access through my property management software. They allow the user to define fields in there software. However they don't show up as specific fields in the database. The database has 2 tables related to them propuserdefined and propuserdefinedvalues. The propuserdefined contains all the info about the field(id, name, description) the propuserdiefinedvalues contains the values associated with the property. It has propid, userdefinedid, and value. My question is this, I need to access the value of propuserdefinedvalues where propid equals the propid in my for property in properties statement and the userdefinedid equals 49. How would I do this? Is it with a template tag?
Thanks in advance for your help.
Brandon
Here are my models.
class Propuserdefined(models.Model):
userdefinedid = models.IntegerField(primary_key=True)
name = models.CharField(max_length=50L, blank=True)
type = models.IntegerField()
userid = models.IntegerField(null=True, blank=True)
updated = models.DateTimeField(null=True, blank=True)
datecreated = models.DateTimeField(null=True, blank=True)
combolist = models.TextField(blank=True)
class Meta:
db_table = 'propuserdefined'
class Propuserdefinedvalues(models.Model):
userdefinedid = models.IntegerField()
propid = models.IntegerField()
value = models.TextField(blank=True)
userid = models.IntegerField(null=True, blank=True)
updated = models.DateTimeField(null=True, blank=True)
datecreated = models.DateTimeField(null=True, blank=True)
class Meta:
db_table = 'propuserdefinedvalues'
class Property(models.Model):
propid = models.IntegerField(primary_key=True)
name = models.CharField(max_length=35L, blank=True)
shortname = models.CharField(max_length=6L, blank=True)
street1 = models.CharField(max_length=50L, blank=True)
street2 = models.CharField(max_length=50L, blank=True)
city = models.CharField(max_length=50L, blank=True)
state = models.CharField(max_length=2L, blank=True)
zip = models.CharField(max_length=50L, blank=True)
phone = models.CharField(max_length=21L, blank=True)
fax = models.CharField(max_length=50L, blank=True)
email = models.CharField(max_length=255L, blank=True)
manager = models.CharField(max_length=25L, blank=True)
billname1 = models.CharField(max_length=35L, blank=True)
billname2 = models.CharField(max_length=35L, blank=True)
billstreet1 = models.CharField(max_length=50L, blank=True)
billstreet2 = models.CharField(max_length=50L, blank=True)
billcity = models.CharField(max_length=50L, blank=True)
billstate = models.CharField(max_length=2L, blank=True)
billzip = models.CharField(max_length=50L, blank=True)
proptaxid = models.CharField(max_length=35L, blank=True)
rentchargetype = models.CharField(max_length=20L, blank=True)
lastpostdate = models.DateField(null=True, blank=True)
lastweeklypostdate = models.DateField(null=True, blank=True)
comments = models.CharField(max_length=25L, blank=True)
enablespeciallatecharge = models.IntegerField(null=True, blank=True)
fixedlatecharge = models.IntegerField(null=True, blank=True)
fixedlateamount = models.FloatField(null=True, blank=True)
fixedlaterentonly = models.IntegerField(null=True, blank=True)
percentlate = models.IntegerField(null=True, blank=True)
percentlateamount = models.FloatField(null=True, blank=True)
percentlatefullcharge = models.IntegerField(null=True, blank=True)
percentlaterentonly = models.IntegerField(null=True, blank=True)
perdaylate = models.IntegerField(null=True, blank=True)
perdaylateamount = models.FloatField(null=True, blank=True)
perdaylategrace = models.IntegerField(null=True, blank=True)
perdaylategracenum = models.IntegerField(null=True, blank=True)
perdatelatelimitamount = models.FloatField()
perdaylategracenonretro = models.IntegerField()
perdaylategraceexclweekends = models.IntegerField()
perdaylategraceexclholidays = models.IntegerField()
datecreated = models.DateTimeField(null=True, blank=True)
updated = models.DateTimeField(null=True, blank=True)
userid = models.IntegerField(null=True, blank=True)
logofile = models.CharField(max_length=255L, blank=True)
merchantid = models.CharField(max_length=255L, blank=True)
epaybankid = models.IntegerField()
epaylimit = models.FloatField()
epayenabled = models.IntegerField()
achconveniencefeeenabled = models.IntegerField()
ccconveniencefeeenabled = models.IntegerField()
rwaachconvenciencefeeenabled = models.IntegerField()
rwaccconveniencefeeenabled = models.IntegerField()
epayislimited = models.IntegerField()
epayusedefaults = models.IntegerField()
achconveniencefee = models.FloatField(null=True, blank=True)
ccconveniencefee = models.FloatField(null=True, blank=True)
rwaachconveniencefee = models.FloatField(null=True, blank=True)
rwaccconveniencefee = models.FloatField(null=True, blank=True)
epaychargetype = models.IntegerField()
epayamounttype = models.IntegerField()
epaysetamount = models.FloatField()
epaycustlimit = models.FloatField()
sqft = models.IntegerField()
lateminbalance = models.FloatField(null=True, blank=True)
defaultbank = models.IntegerField()
postday = models.IntegerField(null=True, blank=True)
active = models.IntegerField(null=True, blank=True)
iscommercial = models.IntegerField(null=True, blank=True)
assignedissueuserid = models.IntegerField(null=True, blank=True)
altname = Propuserdefinedvalues.objects.filter(userdefinedid=49)
class Meta:
db_table = 'property'
It sounds like you are attempting to do this in a Django template. You should instead be using Python code, because Django templates are not designed for this.
The Django models for the table also won't provide the nicest interface for accessing these properties. Instead you should create some functions on top of them. Alternatively you could write raw SQL that do joins across the two tables.
Using your models as they are (there are no ForeignKeys defined, so you can't use the ORM to follow relationships), you can get the details like this (if I understood your question correctly):
property = Property.objects.get(name='my_property_name') # or however you get the property
prop_user_defined_values = Propuserdefinedvalues.objects.filter(propid=property.id, userdefinedid=49)
However, this could be shorted if you changed the order of your models, and some of your fields to type ForiegnKey:
class Property(models.Model):
# ... rest truncated ...
class Propuserdefined(models.Model):
# ... rest truncated ...
class Propuserdefinedvalues(models.Model):
property = models.ForeignKey(Property, db_column='propid')
userdefined = models.ForeignKey(Propuserdefined, db_column='userdefinedid')
# ... rest truncated ...
This would let you do something like:
Propuserdefinedvalues.objects.filter(userdefined__name='my_name', property__name='my_property')
# or:
my_property = Property.objects.get(name='my_property')
Propuserdefinedvalues.objects.filter(userdefined__userdefinedid=49, property=my_property)
I suggest you read about Django's models here: https://docs.djangoproject.com/en/1.5/topics/db/models/ - they're quite easy to get right, even if you have pre-existing tables, as long as you know the relationships.
(Disclaimer: untested code! May be bugs ;))

Categories

Resources