Linking Foreign key between tables in django models - django

I am just starting out with Django and so please help me out with my doubt. Currently, I have three tables Topic, Webpage, and AccessRecord. In the third table AccessRecord, I have used a ForeignKey with the second table Webpage. But Webpage table has three attributes topic, name, and URL ..so my doubt is which attribute of these three will be treated as a Foreign key to AccessRecord table.
Any help will be greatly appreciated.
class Topic(models.Model):
top_name = models.CharField(max_length=264,unique = True)
def __str__(self):
return(self.top_name)
class Webpage(models.Model):
topic = models.ForeignKey(Topic,on_delete=models.CASCADE)
name = models.CharField(max_length=264,unique=True)
url = models.URLField(unique=True)
def __str__(self):
return self.name
class AccessRecord(models.Model):
name = models.ForeignKey(Webpage,on_delete=models.CASCADE)
date = models.DateField()
def __str__(self):
return str(self.date)

Actually, the ForeignKey relationship is from AccessRecord to Webpage object, that only resides in AccessRecord. There is no direct relation between a Webpage to a AccessRecord object. Instead, django provides a reverse relationship where you can access queryset of AccessRecord from a Webpage object. Like this:
webpage = Webpage.objects.first() # an object
records = webpage.accessrecord_set.all() # a queryset
If you iterate through records variable given above, you shall get AccessRecord object. Like this:
for record in records:
print(record) # an AccessRecord object

Truth is the foreign key relationship goes this way
Topic -(1------many)->webpage --(1------------many)-> Accessrecord
In this situation, Accessrecord is also linked to Topic through webpage.
Django will also automatically create a primary key for you.
I also notice something intriguing while playing about with Django ORM.
Note that what you set on the def str: method is what the foreign key field will be filled with.
i.e.
your model webpage returns self.name, the field name from inheriting model Accsessrecord will have options from the self.name.
It cant be filled manually, this way Django maintains data integrity.
You can play around it on your admin page as well.
Hopelly this also helps

Related

How to get all id from model in django [duplicate]

I have a table called user_info. I want to get names of all the users. So the table has a field called name. So in sql I do something like
SELECT distinct(name) from user_info
But I am not able to figure out how to do the same in django. Usually if I already have certain value known, then I can do something like below.
user_info.objects.filter(name='Alex')
And then get the information for that particular user.
But in this case for the given table, I want to get all the name values using django ORM just like I do in sql.
Here is my django model
class user_info(models.Model):
name = models.CharField(max_length=255)
priority = models.CharField(max_length=1)
org = models.CharField(max_length=20)
How can I do this in django?
You can use values_list.
user_info.objects.values_list('name', flat=True).distinct()
Note, in Python classes are usually defined in InitialCaps: your model should be UserInfo.
You can use values_list() as given in Daniel's answer, which will provide you your data in a list containing the values in the field. Or you can also use, values() like this:
user_info.object.values('name')
which will return you a queryset containing a dictionary. values_list() and values() are used to select the columns in a table.
Adding on to the accepted answer, if the field is a foreign key the id values(numerical) are returned in the queryset. Hence if you are expecting other kinds of values defined in the model of which the foreign key is part then you have to modify the query like this:
`Post.objects.values_list('author__username')`
Post is a model class having author as a foreign key field which in turn has its username field:
Here, "author" field was appended with double undersocre followed by the field "name", otherwise primary key of the model will be returned in queryset. I assume this was #Carlo's doubt in accepted answer.

Django how to implement a one-to-many self-dependent foreign key

I am implementing a User referral system, which existing users can refer other people to register an account with the link they provided. After the new user registers, the new user will be stored to the field 'referred_who' of the existing user.
I have tried using the following method:
class CustomUser(AbstractBaseUser):
...
referred_who = models.ManyToManyField('self', blank=True, symmetrical=False)
class ReferralAward(View):
def get(self, request, *args, **kwargs):
referral_id = self.request.GET['referral_id']
current_referred = self.request.GET['referred']
// referrer
user = get_user_model().objects.filter(referral_id=referral_id)
// user being referred
referred_user = get_user_model().objects.filter(username=current_referred)
for item in user:
previous_referred = item.referred_who
previous_referred.add(referred_user[0])
user.update(referred_who=previous_referred)
And I got the following error:
Cannot update model field <django.db.models.fields.related.ManyToManyField: referred_who> (only non-relations and foreign keys permitted).
I am not sure if this method even works. I have check the Django Admin backend and I realized the 'Referred who' field actually contains all the users. It seems that it only highlightes the user being referred instead of only showing the referred users.
Also, I tried to access the 'referred_who' field in the back-end and it returns 'None'.
Is there a way to stored the users in the 'referred_who' field so that I can see all of the user being referred and access them in the back-end? For instance:
referral_id = self.request.GET['referral_id']
user = get_user_model().objects.filter(referral_id=referral_id)
print(user[0].referred_who)
Can someone show me a better way to do it? Thanks a lot!
You ask how to create a 1-Many field, but in your models you're trying to create m2m. Just change field to FK.
referred_who = models.ForeignKey('self', blank=True).
In case you need to have multiple fks to the same model, you need to specify related_name as well. You can use name of the field for it. More in docs.

Foregin key field name is followed by _id

In a model, when a foreign key field is created then Django apparently create another field with the same field name followed by _id.
for example if I have
class Post(models.Model):
user = models.ForeignKey(User,on_delete=models.CASCADE,default=None)
dated = models.DateTimeField(auto_now=True)
...
Then I will have the following fields available:
id,user,user_id,dated
I am not sure why this field (user_id) was added?
Later I wanted to override my queryset in a class view
so I was confused which one to use (user field or user_id field)
:
def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(user_id=self.request.user.id)
Or
def get_queryset(self):
queryset = super().get_queryset()
return queryset.filter(user=self.request.user.id)
I tried both and both worked just fine
My question is:
1) What is the purpose of creating this additional field ?
2) What is the difference between the original foreign key field (user in my case) and user_id field?
3) Will both fields user and user_id available in the database? what is the point of that?
4) Is the content of user and user_id identical in each record? if so ,then what the purpose of this additional field that was created automatically by django?
Thanks a lot
Django only creates one column in the database for the foreign key.
The difference between the field and the _id attribute it generates is that accessing the field performs a query for the full set of columns from the related table in order to construct the complete related object. If you want the full object, use the field (and probably also use select_related() in the initial query to save you from doing N+1 queries).
On the other hand, if all you need is the DB-level value of the foreign key, which is usually the primary key of the related object (and often that is what you want), the _id attribute shortcut is there for you and already has the data, because that's what was actually in the foreign key column.
In other words, suppose I have models like this:
class ModelA(models.Model):
name = models.TextField()
class ModelB(models.Model):
name = models.TextField()
a_instance = models.ForeignKey(ModelA)
If you query for a ModelB, like ModelB.objects.get(pk=12), you'll get a query like this:
SELECT id, name, a_instance_id
FROM your_app.modelb
WHERE id = 12;
Notice a_instance_id is the name of the column -- it's just a foreign key, all it stores is a pointer to the primary key of a ModelA instance. If you just need that primary key, accessing the a_instance_id attribute has it already without needing to do another query. If you access the a_instance field, though, you get to do another query:
SELECT id, name
FROM your_app.modela
WHERE id = (whatever the value of that foreign key was);

How do you save a relationship with an existing tag otherwise create the tag, in django views?

In a many to many tagging system; how do you save a photo to the name of the tag already exists and still save the photo with the rest of the tags added through the form.
class Photo(models.Model):
title = models.TextField(blank=False, null=True)
path = models.FileField(blank=False, null=True, upload_to='photos/')
class Tag(models.Model):
name = models.TextField(blank=False, null=True)
photos = models.ManyToManyField(Photo)
Heres an example from the view that will raise an error with Tag.name unique=true.
Granted there exists a photo foo with a tag bar.
photo = Photo(title='foo2')
photo.save()
bar = form.data['tag'] ** this is tag bar
bar = Tag(name=bar)
bar.save() **will raise error, name bar already exists so you cannot save it
bar.photos.add(instance)
I apologize for reviving this old question and being contrary, but I thought it might help for me to provide a full code snippet. I've mentioned get_or_create a couple times but either you're misunderstanding me or vice versa. This code should be the same as what you wrote, except it uses Django's get_or_create rather than reinventing it and needing to catch the IntegrityError:
instance = Photo(title=form.data['title'])
instance.save()
tag, created = Tag.objects.get_or_create(name=form.data['tags'])
tag.photos.add(instance)
return HttpResponse("Succesful Upload with Tags")
Hope this helps. :-)
Unless I'm mistaken, the tag 'bar' will be connected to both foo and foo2. The way ManyToMany fields in Django work is by creating an extra table, where each entry has a foreign key to both of the things it connects to.
So, in your example, it would create a table photo_photo_tag, with a foreign key to a Photo and a foreign key to a Tag. Thus, the tag itself is only saved once in the tag table, but referenced by foreign key many times in photo_photo_tag.
For more information on how ManyToMany works, see the following:
https://docs.djangoproject.com/en/dev/topics/db/examples/many_to_many/
Django - enforcing ManyToManyField unique items
What I ended up doing was, adding setting the Tag model's name to unique. Then in the view doing a try on the save. If the name exists; it will still save the relation to that tag names id
instance = Photo(title=form.data['title'])
instance.save()
tag = form.data['tags']
tag = Tag(name=tag)
try:
tag.save() **if the tag is unique, it will save it then add relation
tag.photos.add(instance)
except IntegrityError, e: ** if its not unique, grab the ID of that tag
tag_id = Tag.objects.all().filter(name=tag)
singletag = tag_id[0]
singletag.photos.add(instance) ** then add relation
return HttpResponse("Succesful Upload with Tags")

Django models: Reference field return multiple type of models

As a project to figure out Django I'm trying to build a small game.
A player has a base. A base has several type of items it can harbor. (Vehicle, Defense, Building).
I have 3 static tables which contain information for the first level of each item (in the game these values are used in formulas to calculate stuff for upgrades). I've used a sequence to insert all these items in these different tables so the ID's are unique across tables.
To keep track of what items the player has per base I have a table 'Property'. I want to use a single field as a reference to the ID of an item and trying to get this done with the Django models.
Warning: my knowledge about Django models are pretty limited and I've been stuck with this a few days now.
Is this possible and if so how can it be done?
I tried using annotations on the save method to change the value of a field by overwriting the field with the id of that object before trying to query the object by id when trying to 'get' the object, however I can't get past the obvious restriction of the model when defining that field as an Integer - I hoped it wouldn't validate until I called save()
def getPropertyItemID(func):
"""
This method sets the referral ID to an item to the actual ID.
"""
def decoratedFunction(*args):
# Grab a reference to the data object we want to update.
data_object=args[0]
# Set the ID if item is not empty.
if data_object.item is not None:
data_object.item=data_object.item.id
# Execute the function we're decorating
return func(*args)
return decoratedFunction
class Property(models.Model):
"""
This class represents items that a user has per base.
"""
user=models.ForeignKey(User)
base=models.ForeignKey(Base)
item=models.IntegerField()
amount=models.IntegerField(default=0)
level=models.SmallIntegerField(default=0)
class Meta:
db_table='property'
#getPropertyItemID
def save(self):
# Now actually save the object
super(Property, self).save()
I hope you can help me here. The end result I'd like to be able to put to use would be something like:
# Adding - automatically saving the ID of item regardless of the class
# of item
item = Property(user=user, base=base, item=building)
item.save()
# Retrieving - automatically create an instance of an object based on the ID
# of item, regardless of the table this ID is found in.
building = Property.objects.all().distinct(True).get(base=base, item=Building.objects.all().distinct(True).get(name='Tower'))
# At this point building should be an instance of the Building model
If I'm completely off and I can achieve this differently I'm all ears :)
I think you are looking for a Generic Relationship:
class Property(models.Model):
user=models.ForeignKey(User)
base=models.ForeignKey(Base)
content_type = models.ForeignKey(ContentType) # Which model is `item` representing?
object_id = models.PositiveIntegerField() # What is its primary key?
item=generic.GenericForeignKey('content_type', 'object_id') # Easy way to access it.
amount=models.IntegerField(default=0)
level=models.SmallIntegerField(default=0)
This lets you create items as you mentioned, however you would probably need to look at a different way of filtering those items out.