Generic Foreign Keys in Django / GenericForeignKey / GenericRelation

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this video we're going to take a look at generic foreign keys in Jango now in the previous video we looked at the content types framework and we looked at the content type model that's provided by that framework that allows you to look up any other model in the D Jango application in this one we're going to look at generic forign keys and these allow you to add a forign key from any of the models that you have in your application to the content type model and this allows your model to effectively tie itself to any other class in the jangle application let's dive in and we're going to see how to do that now now I'm going to start by referencing the documentation for jangle for generic relations so when you add a foreign key from a model in your application to the content type model this is going to allow you to connect your model to any other model class in the application and that's what Jango does with the permission model for example we'll see that later in the video and when you use the content type as a foreign key you can enable these truly generic and these are also called polymorphic relation relationships between models now I'm going to scroll down to this code section and I'm going to make this smaller so that these appear on one line and we're going to see how this works using this code and then we'll build a practical example now what we have here is a model called tagged item and the idea here is that you might want to add a tag to any number of other models in the application for example a movie might have tags a user might have tags that they've applied to themselves and so on you might have tags for multiple models so what you can do is provide a generic foreign key so how do you do that first of all you define a few Fields here the first one is called content type and that's a foreign key to the content type model that we saw in the previous video which should be appearing on the screen now and the content type field will therefore link the model that we have here to that content type model and then we also have an object ID field and the object ID is a positive integer field and when we link this model to a particular content type the object ID field is is going to be a number that tells this particular model which instance of that model do we want to Fitch and the final thing we have is a field called content object and that is a generic foreign key which we've imported at the top from Django do contrib do content types. Fields so we have a generic foreign key field here and we pass in the reference to the content type and the object ID fields that we Define above that in this model so let's now move on and we're going to look at a practical example here let's go to vs code where I have the project open and we have a script here and we have a models.py file that contains some models we're going to create a new model in the app called comment that stores user comments in this imaginary system and we want to be able to associate a comment with restaurants so for example a user goes to a restaurant and they write a comment about that restaurant and we also want to maybe associate a comment with a particular rating so we have a rating model we may want to add comments underneath the rating so we need the comment model that we're about to create to be able to reference either one of those models as the parent foreign key and we're going to use a generic forign key for that so what I'm going to do is scroll to the bottom of this models file and I'm actually going to remove the dummy model that we had in the previous video and we're going to replace that with a new model called comment that will inherit from the jangle model class and a comment of course it contains text and we're going to set that equal to a models. text field and then we need to add the reference to that content type and that's going to be a foreign key to the content type model in jangle that we saw in the previous video and when we delete a model we want that to Cascade so models. Cascade now I will import the content type model in a second we also need the object ID here and that's going to be a models. positive small integer field that's going to track the actual instance ID that we're connecting the comment to for the given content type and the final field we need to add is the content object and that's going to be an instance of the generic foreign key and again we're going to import this in a second but like the documentation we need to pass the reference to the content type as the first parameter and the name of the field that we used and the standard name is content type so we pass that in there and we also pass in the object ID so that Jango will then know which particular instance of that content type to Fitch and then once we've done that at the top I'm going to import the two objects that we're using so from the fields module in the content types framework we're importing generic forign key and from the models we will import the content type model so let's save this and I'm going to go back down to the comment model so again let's highlight that we have a foreign key to the content type model and that's the model that tracks all of the other models that we have in the jangle application which will allow us to link this comment to any other model and to know which instance that we're linking it to we have the object ID and as far as the generic foreign key goes we're going to see what benefits that provides later in the the video now what I'm going to do is I'm going to go to the admin.py file in this Jango application and we've already registered a bunch of models in this file what I'm going to do is register a new one for the comment model and we're going to create some model admin subclasses here so what I'm going to do for the restaurant model is create a new admin class called restaurant admin that's going to inherit from admin. modeladmin and what we need to provide to that is a few Fields here now I'm going to provide a list display field so for for a restaurant we want to list out the ID of the restaurant and the name of the restaurant as well and then we can add the restaurant admin as a second argument to admin. site. register so we're passing that in to tell this function that we want to customize the admin for the restaurant model I'm going to do the same for the rating model so I'm going to copy this and we're going to call the the class itself rating admin and then we pass that into this rating model here and we need to change the fields that we're referencing the rating does doesn't have a name field but it does have a numerical rating field now the final thing I'm going to do is add a new admin class for the comment model that we've just imported at the top and we're going to use this to see what those fields provide in a second so let's create a class called comment admin and we're going to inherit again from model admin and we're going to add a list display to that and I'm going to pass all of these fields in so we have the text field for the actual text for that comment and we also have the fields that we use to create the generic foreign key once we've done that what I'm going to do is I'm going to run the make migrations command and you can see that's created the new comment model and remove that dummy model that we had before we can then run migrate to actually affect those changes to the database so that's going to create the comment table what I'm going to do after that is run the Jango server and we're going to go to the admin UI so we're on the admin page I'm going to go to the restaurants page here and we can see now on the list display which is this page we get the ID and the name of of the restaurant previously we just got the dunder string output but now we have the ability to customize that and add as many fields as we want to this table now we need to access the comment model and I've forgotten to register that so let's copy this and it's the comment admin that we need to add to admin. site. register so we're registering the comment model and we're passing that custom admin class if we now go back and refresh this we can see the comments has they have appeared sorry in the Django admin and we can add a comment with this button at the top right and let's say we're adding a review for a restaurant so we could say really good food probably something more exciting would be good but let's just go with that and the content type this is where we can actually link the comment to any other model in the application we're going to link it to the core restaurant model and we need to provide an object ID so that's why I added the IDS to the list display if we go to the restaurant page on a new tab you can see the IDS for each of the restaurants so so what we need to do is we need to provide one of these IDs to this object ID field so let's add that and we're going to pass number three in here and then we can save this and we then get this output here so we get the text we also get the object ID and the content type but for the content object which if you remember was the instance of the generic forign key field for that particular field what we have is the text Golden Dragon and that's the name of the restaurant what this actually represents is essentially the object that's being represented by this generic foreign key so this is an actual instance of the concrete model that's represented by this content type so it's the restaurant with the ID of three what we can do is add another comment and say something like I agree with this rating and let's say that this comment is being added to a rating that was added to the database so we have a core rating content type and let's give this object ID of two here if we hit save we get back the second instance of this comment but you can see now that that is actually applied this foreign key to a different underlying model than the original comment so the original comment was to a restaurant this one is to the rating content type and the content object therefore will point to an actual rating in the database so this structure allows you to apply or allows you to add foreign keys generically from one class to any other class in your application any other model class and this allows you to create flexible relationships in your application where you can add links from one model to potentially any other model now what I'm going to do to demonstrate this further is we're going to query this using some code in jangle so let's go back to vs code and go to orm script.py at the top we've imported the content type model I'm also going to import the new comment model that we just created and let me make the terminal smaller within the run method what we're going to do do is fetch all of the comments so let's run the query comment. objects. all that will give us back all of the comments and then we can look over them so for comment and comments what we can do is print out comment and then we need a field here we can access a field for each comment what I want to print out just so that we can see this is the content object so let's save this and if we go back to models. Pi remember the content object that's the actual instance of this generic foreign key field so so we can save this we can stop the server and make the terminal bigger here and I'm going to run Python manage.py runscript and we pass the orm script into this and we're going to get the output here and we can see we have the restaurant on line one and the rating on line two and we can look at the type of each of these so if I check the type of each of these instances we can see we get back the actual underlying model it's not the content type model that represents the class this is the actual instance of one of the these classes now let's move on we're going to see how jangle takes a comment object and then it finds the correct instance for this content object so remember the content object can be any other model in the application we want to find out how D Jango can take the comment and look that up correctly to get the correct relationship so at the top I'm going to change the code a little bit and we're going to run the query comment doobs do and we'll rename the variable comment because we're only fetching one comment and what we can do is get the content type so in order to get that what we can do is print comment. content type not content object which is the generic foreign key this time we're printing the content type and then I'll add another comment here from the content type that we have above we can fetch the associated generic foreign key instance so what I'm going to do actually is pull out the content type into a variable called C type so that's going to be comment. content type and we can change the print statement below that to print that out and from that content type type model instance we can then look up the actual concrete model using a method that we saw in the last video so what I'm going to do is I'm going to create a variable called model and it's going to be equal to the content type instance and we're going to call the function get object for this type and this is similar to the model doobs doget function that you would use to look up a single instance of a model for this function though it's going to take the parameters that we pass in here and it's going to look up the model associated with the content type so so what we need to pass in here is a primary key for the related model and we're going to set that equal to the object ID so the comment that we pulled out from the database that has that object ID field that represents the ID of the instance that we're linking the model to we can pass that in as a lookup with the primary key field to this get object for this type function and then we can print the model to the terminal and we can also print the type of the model as well just so we can see what the actual type of that returned model is so let's now run this and see what kind of output we get first of all we get the content type printed out and then we get the actual instance printed out and that instance is coming from this function here we take the content type we look up the object for that type and remember the content type represents the restaurant model in this case so the lookup parameters that we pass in here they are actually going to be passed into the restaurant lookup and that's going to give us back a single instance of the restaurant model and that's what we see here in this output and we also get the type here and we can see it's an instance of the restaurant model so that is the code that's kind of run behind the scenes in jangle but this can be simplified by just defining a generic foreign key when you define the generic foreign key and pass the content type and the object ID references in jangle will abstract away all of these lookups so we can remove all of this code and to get access to the related field instead we can just call comment or rather just reference comment do content object and if we run that on the terminal we get back the instance of the restaurant in this case but this will work for any other type so for example the second and the last comment that we had in the database was for a rating if we run this again we get back the instance of the rating and we can get to that just by accessing this content object field that we defined on the model and we made that a generic foreign key now we can also programmatically create a comment by just referencing the content object and not worrying about the content type in the object ID Fields let's see an example of what I mean now we're back in the orm script.py and at the top here what I'm going to do is I'm going to pull a restaurant out from the database and that's going to be restaurant. objects. first and I need to import the restaurant model at the top so we have a restaurant and let's say we want to add a comment to that particular restaurant I'm going to change this line of code here instead of comment doobs do last let's call the do create function we're going to create a new comment in the database and we need some text for that comment so let's add some text here aul restaurant and in order to link this comment to the restaurant through the generic foreign key we can actually just pass the content object in here and Link that to the restaurant that we pulled out on the line above we can then print that to the terminal just below here and I'm also going to print the instance dictionary here so comment doore dictionary if we save that we can then run the application on the terminal so I'm going to run this and let's see what output we get so we get a new comment object but if we look at the fields that have been added we can see the text for awful restaurant but we also have the content type ID and the object ID so jangle has automatically filled that in to create the generic foreign key all we need to do is to the field that we Define the generic foreign key on we need to pass the instance of the other model that we want to connect it to so it's very easy Once you have a generic foreign key to link other objects to that model you can just pass the instance in and and it will do all of the work under the hood let's now move on to reverse generic relations now to begin with at the moment we have a generic forign key from the comment model to any other model that that can be done that can be applied to any other model in the application now typically when you have these generic relations you have a core set of models in mind that you want to connect this model to and for us a comment belongs either to a restaurant or a rating now if you know which set of models are typically going to be the parent and the generic foreign key we can add a generic relation on those parent models and that allows them to link back to the comments so for example we could take an instance of a restaurant and then find all of the comments for that restaurant using the generic relation so let's do that right now we're going to go to the top and import from the fields module the generic relation and we're going to add to both the restaurant and the rating model another field and that's going to be a generic relation called comments so we're going to make an instance of that generic relation and what we need to pass in here is the name of the model class that we want to be the other side of that relation and that's going to be the comment model so we can copy this and bring it down also to the rating model so we're going to add that new field in both restaurant and rating after we do that we can then query from these parent models to get all of the associated comments so let's go back to omm script.py and what we're going to do is we're going to get a particular restaurant from the database and we can do that with the do get function and I'm going to go back to the jangle admin now and I'm going to go to the restaurants page now we have the IDS for all of the restaurant we need to find a restaurant that has comments in order to do this example so let's go to the comments page and we have two comments that belong to the restaurant model and the object ID is four and three so let's get the one associated with the ID of three so we're going to get the restaurant using the doget function we pass the primary key of three in and what we're going to do now is we're going to fetch all all of the comments for this restaurant now because we've added that generic relation we can get all of the comments and let's create a variable called comments simply by referencing the restaurant and then following that generic relation which remember we created the field called comments and we can then call do all that's going to give us back all of these comments and then we can print them out to the terminal so let's stop the Jango server and run this script and you can see we get back a query set containing all of the comments that are linked to that restaurant and under the hood that is going to use the generic foreign key that we have but it's going to look on the reverse side of the relation from the parent model to get all of the child models and if we start the server and go back to the Django admin here and go to this particular comment with the ID of four I'm going to change that to three and then save this so now we have two comments that are associated with the restaurant with ID of three we can go back to the terminal stop this again and rerun the script and this time we're going to get back a query set containing two comments so we now have this link between the restaurant and all of its comments and this is similar to a many to many manager in a way you can also add and remove comments using that generic relation so for example to add comments for this restaurant I'm going to change the all to the dot add function again similar to a many to many manager and then to that we can pass in an instance of a comment so I'm just going to create one dynamically here comment. object.create and let's give the comment some text and I'm just going to say text one and then in order to create the comment we can also pass the content object that it's related to and in this case that's going to be the restaurant that we pulled out with the ID of three and we don't need the variable comments here I'm going to remove that and we're just going to comment this print statement out and save that we can then rerun the script and that is going to dynamically add a new comment to the set of comments that are associated with the restaurant and under the hood the generic relation is going to take care of creating this com comment linking it to the restaurant and setting up the correct content type and object ID references now if we go back to the jangle admin and refresh this page we can see we now have another comment that's been added to this page and again it's linked to a restaurant with the ID of three and just like the many to many manager we can actually do other operations on this generic relation from the parent to the child so for example we can get the number of comments associated with a restaurant with the restaurant. comments. count function and I'm going to print that to the terminal and let's say we wanted to get the last comment for this restaurant and remove it from the database let's see how to do that now we're going to create a variable called last comment that's going to be equal to restaurant. comments. last once we have that we can then use the restaurant model and we can use that generic relation to the comments and we can call the dot remove method we need to pass an instance in to remove a comment so we're going to pass the instance for for the last comment and then just to make sure that's been deleted I'm going to replicate the call to do count and we're going to print that to the terminal and then let's run this and hopefully we're going to see that the count goes down by one and we do see that we started off with three comments and we finished with two and that's because we removed a comment using this remove method that's defined on the generic relation now we can also add a related name to our generic relations I'm just going to show an example of that and then I'm sure you'll understand how that's done so I'm going to go up to the restaurant model we have a generic relation for the restaurant model but we can add another keyword argument here called related query name and this can be used in filtering statements as we're going to see now so let's add a related name here of restaurant the comment can be associated with a single restaurant so we'll keep that in the singular form and I'm going to save this and we're going to see how to use this now by going back to the script and I'm actually just going to remove all of this code and what we're going to do is create a variable called comments and that's going to be equal to the comment model we're going to use the doobs do filter method so we're going to filter down these comments and let's say we wanted to get only comments that were associated with Indian restaurants now if we look at the comment model we can see we have a Content object and that represents an instance of the associated model but the question is how would we filter these comments to only get Indian restaurants what's the easy way to do that now that we've added to the restaurant model this related query name of restaurant we can actually reference that in the filter statement so I'm going to pass that in restaurant and remember that's not a field on the comment model that's a related query name and then we can filter that down further by following that reference to the restaurant and then filtering to the restaurant type and we're going to make that equal to the restaurants type choices. indianfield we can then print those comments to the terminal now I'm not sure we have any comments associated with Indian restaurants so this might return an empty query set but if we go back to the Jango admin you can see that the two comments for our restaurants are attached to a restaurant called Golden Dragon which is a Chinese restaurant let's go back to vs code and we're going to change the Indian reference to Chinese here and hopefully when we run the script now we're going to get back the two comments that are tied to that particular restaurant type so in the do filter statements we can reference the related query name simply by defining a generic relation on the other side of the generic foreign key and then passing a related query name into that that we can make equal to a string where we can then use that in our filter method to filter down the comments by that particular parent class now as a final example let's see how to create generic relations in the jangle admin interface let's start the server and go back to the jangle admin and I'm going to go to the restaurants page here and if we click add restaurant you can see we get back this form containing all of the fields for the restaurant but we don't have an option here of adding any comments so the comment even though there's a generic relation from the restaurant back to the comments we don't have the ability to add those comments here in the admin but we're going to show how we can do that now what we can do is we can add these comments in line by creating something called a tabular inline class in the Jango admin and there's a page I'm going to reference here on using generic relations as an inline I'll link that below the video and if we scroll down here we get a snippet of code that we can add to the admin.py file and I'm going to copy this import from the content types. admin module we have this generic tabular inline class let's go back to vs code and I'm going to minimize the terminal and I'm going to open the admin.py file and at the top of this let's import that generic tabular inline class and what I'm going to do just at the top here is I'm going to create a comment in line model and that's going to inherit from that generic tabular in line and what we need to do is provide a model to this class so it's going to be the comment model and and we can provide a maximum number of related instances to create in the form I'm going to set that equal to one for now once we've created the comment in line we can go down to the restaurant admin model class so we've already added a list display field but we can add another field to this model to this class rather called in lines and we're going to set that equal to a list containing that single comment in line class that we created just above here and this in lines field this can be added to any admin class class where it makes sense for a comment to be added as a child for that class now what's the effect of adding the in lines to the restaurant admin let's go back to the Django admin now and back to the ad restaurant form if I refresh this page we're going to see a new widget at the bottom that allows us to add a comment so when we add a new restaurant that's going to create the restaurant in the database and give it a particular ID and then we can create the comment in line and Jango is automatically going to fill in those details to create the generic for key for example it's going to tie it to the restaurant content type and it's going to set the object ID to the new object ID created for this new restaurant in the database so if you need to add some generic relations in the jangle admin you can import this generic tabular in line create a class and tie it to a particular model and then within your parent class admin you can then create an inline and add your inline to that list and we can also change the maximum number of potential comments to be added here so if I set that equal to four and go back to the page if we refresh this page now we can see we get four of these so you can customize these inline admin classes as you wish so that's the end of the Practical examples what I'm going to do to finish the video is look at the jangle permission system and we're going to see how jango's permissions framework uses content types and generic foreign keys so let's look at this page of the documentation which I'll leave a link to in the video notes we have a page on permissions and authorization so jangle has a built-in permission system and it provides a way to assign permissions to specific users and groups of users now jangle comes with a set of default permissions if we scroll down here we can see these default permissions so when you have the oth module listed in installed apps Jango is going to ensure that there are four default permissions so we have the add permission the change permission delete and view permission and these are created for each Jango model in your installed applications and the permiss will be created when you run the my great command and we can see these by opening up the database browser for SQL light and I have that open and we have a page here for the off permission models so if I browse that table you can see we have all of these permissions in this database table and for each model we have an add a change a delete and a view permission now something to notice in this table is this column here you can see that we have for each permission a link to a content type ID so what is that that exactly let's go back to the database structure and we're going to right click the off permission table and I'm going to go to modify table so we can have a look so the content type ID here it's a foreign key to the jangle content type table so when we go back to the data in that table for this particular permission let's say the ad group permission we have a content type ID of three and if we go back to the jangle content type table here we can see that that links to the content type associated with the group model now how does Jango link a particular user to a particular permission let's go back to the database structure and we're going to look at this table here it's called o user user permissions let's rightclick that and browse the table and you can see that this has a few columns so there's a user ID column that's simple it's a foreign key to the O user table and we also have a permission ID as well and that's a foreign key to the permission table which is this one here and each permission has has a content type ID so if you assign a permission that's linked to a user that permission is applied to a particular model in the Django application and that's done through this content type ID which links the permission to its parent content type and this structure allows us to link these permissions to users and it allows a flexible system where different users can have different permissions for different models in the application now I'm not going to show a practical example of this now but if you're interested leave a comment below the video and we can maybe make a dedicated video to the jangle permission system let us know if that's something you're interested in but I'm just wanting to show the structure of these database tables and how you can flexibly link a permission to a user and that permission that you're linking can also be tied to a particular model class now that's all for this video thanks very much for watching a bit of a technical topic this but it's very useful to understand how you can link one model in your jangle application to any other model in the application please give the video a thumbs up if you've enjoyed it and subscribe to the channel if you've not already done so and we'll see you in the next video
Info
Channel: BugBytes
Views: 4,484
Rating: undefined out of 5
Keywords:
Id: Wt4_7ZAE8dI
Channel Id: undefined
Length: 30min 9sec (1809 seconds)
Published: Fri Dec 01 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.