I have a model with two integer fields which are supposed to be foreign keys. In my admin view, those two fields are editable but I would like to display a search icon next to the field, so I can retrieve the id of another model and append to the field. Does django have this feature?
class Linkage(models.Model):
user_id = models.IntegerField(default=1)
parent_id = models.IntegerField(default=0)
If your model has 2 fields that should actually be foreign keys, then you need to use ForeignKey instead of IntegerField as so:
from django.db import Models
from app.models import yourModel
class Linkage(models.Model):
user_id = models.ForeignKey(yourModel)
parent_id = models.ForeignKey(yourModel)
This Django feature will take care of linking your Linkage to your other model of choice automatically. In the admin, you will no longer need to set an integer with the id of the foreign key, you will be able to simply select an instance of the linked model.
You can read more about Django fields references here.
EDIT: In the way you designed yoour models, there might for example not be a parent_id, so you can add options such as null=True to the parent_id field as so:
parent_id = models.ForeignKey(yourModel, null=True)
I strongly recommend that you read the Django documentation, it is really good for beginners as it is very detailed and clear. You can access the documentation here.
They also have a set of tutorials to get you through the steps and introduce you to the different basics you need to know to get started with Django here.
Related
I've been reading through the Django documentation and looking over some of the other answers on the site for a couple of hours now, yet I still can't get it to sink in. I know this isn't Django specific, but the examples I use will be from a Django project.
My question boils down to when is it appropriate to use each:
Many-to-many relationships
Many-to-one relationships
One-to-one relationships
One-to-one, more or less makes sense to me.
Now for the other two. While I understand the differences between them in isolation, when it comes to using them practically in a project, I get confused. Here is an example:
class User(AbstractUser):
pass
class Listing(models.Model):
title = models.CharField(max_length=64)
description = models.TextField()
class Watchlist(models.Model):
user = models.ForeignKey(User, related_name='watchlist', on_delete=models.CASCADE)
item = models.ManyToManyField(Listing)
class Comment(models.Model):
user = models.ForeignKey(User, related_name='comments', on_delete=models.SET_NULL)
comment = models.TextField()
Would this be the correct use of Many-to-one(ForeignKey) and Many-to-many?
Should Watchlist.item be a ForeignKey? Or is M2M correct?
Wouldn't it simplify to make the 'Watchlist' part of the User class? (give them an empty list to populate with listing ID's)
Why is Watchlist.user not a One-to-one relationship, if each watchlist belongs to a single user, and a user can only have one list?
Apologies for my stupidity, I just can't get this to sink in!
Thank you.
edit: Context, the models are from a 'learning' project I was working on intended to be an auction site, similar to eBay. The watchlist is sort of a 'wish' list... for the user to watch an item, not for site to watch a user!
To explain it simply these django-models or objects represents tables in your database and the fields are like the columns in them. So with a one-to-one relation you can only have one row in one table relating to one row in another table. For example one user in the user table (represented by one row) can only relate to one row in a profile table. But your user can have many comments, so this would be a one-to-many/foreignkey relation (if you set unique=true on a fk, it will in practice function as 1:1). If the users can collaborate on writing comments, or for example as here on stackoverflow where users can edit other users comments, that would be a many-to-many relation.
Database design can be complicated/complex, especially using an ORM without basic knowledge of SQL and how it all works beneath. In general it requires a bit of planning even for a simple application.
I have CustomUser model and Post model. I consider adding a lightweight like mechanism to the posts.
What comes to my mind is defining a Like model in such fashion to connect the models to each other:
class LikeFeedback(models.Model):
likingUser = models.ForeignKey(CustomUser)
post_liked = models.ManyToManyField(Post)
But this design produces a new row in the database with each like.
Another option is to define CustomUser and Post models in a way that:
class Post(models.Model):
...
users_liked = models.ManyToManyField(CustomUser)
class CustomUser(models.Model):
...
posts_liked = models.ManyToManyField(Post)
I am not sure if this approach creates a new row or uses a different indexing mechanism, but it looks tidier.
In terms of DB performance what approach is the fastest? Do I need to define the ManyToMany connection in both models to speed up DB processes? Because 15 posts are to be displayed on the webpage at once and and with every post it is necessary to check if the visitor already liked the note. Also, with each like and takeback a write operation is to be performed on the DB.
I am not sure if this approach creates a new row or uses a different indexing mechanism, but it looks tidier.
A ManyToManyField will create an extra table called a junction table [wiki] with ForeignKeys to the model where you define the ManyToManyField, and the model that you target with the ManyToManyField.
You furthermore only need one ManyToManyField, otherwise you make two relations that act indepdently. You thus model this as:
from django.conf import settings
class Post(models.Model):
# ...
likes = models.ManyToManyField(
settings.AUTH_USER_MODEL,
related_name='liked_posts'
)
class CustomUser(models.Model):
# ...
# no ManyToManyField to Post
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
The answer to question Django admin ManyToMany inline "has no ForeignKey to" error refers to the Django Admin documentation. The models given there are:
class Person(models.Model):
name = models.CharField(max_length=128)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, related_name='groups')
and the inline admin classes are:
class MembershipInline(admin.TabularInline):
model = Group.members.through
class PersonAdmin(admin.ModelAdmin):
inlines = [MembershipInline,]
class GroupAdmin(admin.ModelAdmin):
inlines = [MembershipInline,]
exclude = ('members',)
... which allows group membership to be managed from the Person page but not from the Group page. But what if the administrator wants to manage members only from the Group page? Getting rid of the exclude line would allow both pages to manage the relationship, but the Django documentation (probably incorrectly) says "you must tell Django’s admin to not display this widget". What they probably mean is that you "should" tell Django's admin not to display it - nothing bad will happen if you don't, but it's redundant.
So without changing the models, is it possible to exclude the membership widget from the Person page instead of from the Group page? Both obvious attempts:
class PersonAdmin(admin.ModelAdmin):
inlines = [MembershipInline,]
exclude = ('Group.members',)
and
class PersonAdmin(admin.ModelAdmin):
inlines = [MembershipInline,]
exclude = ('groups',)
(the second using the related_name from the model) fail with the error:
'PersonAdmin.exclude' refers to field 'groups' that is missing from the form.
Yes, the model could be changed to put the ManyToManyField under Person. But since it is a symmetric relationship, there is no logical reason why it could not be managed from either Person or Group (but not both) without having to change the database schema. Can Django Admin manage group membership from the group page and exclude it from the person page?
what if the administrator wants to manage members only from the Group
page?
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
pass
#admin.register(Group)
class GroupAdmin(admin.ModelAdmin):
pass
Django by default shows a Person m2m widget in the GroupAdmin. You correctly use the through model to get inlines, but the inlines are a separate definition not affected by the exclude. EDIT: Another simple way to put it is that you only specify the inlines on the Admin where you want them, no need to specify them on the opposite side's Admin.
Using inlines:
from core.models import Group, Person
class MembershipInline(admin.TabularInline):
model = Group.members.through
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
pass
#admin.register(Group)
class GroupAdmin(admin.ModelAdmin):
inlines = [MembershipInline, ]
exclude = ('members',)
(tested on Django 3.0.3)
You don't give a reference for this claim:
the Django documentation (probably incorrectly) says "you must tell Django’s admin to not display this widget".
so I can only refer to the current (1.10) documentation for Django. It currently says of ManyToMany fields in the admin:
Django displays an admin widget for a many-to-many field on the model that defines the relation (in this case, Group). If you want to use an inline model to represent the many-to-many relationship, you must tell Django’s admin to not display this widget - otherwise you will end up with two widgets on your admin page for managing the relation.
So, in response to your correct statement:
But since it is a symmetric relationship, there is no logical reason why it could not be managed from either Person or Group (but not both) without having to change the database schema.
the reason is that the many-to-many relationship has to be defined somewhere; you have chosen to define it on the Group model, so that determines the default admin behaviour. If you want to move it, then you'll need to do a database migration to make that happen.
If, on the other hand, you want this documented behaviour to be different without changing your use of it — you don't seem to be asking a question that fits at StackOverflow. Better to report a bug with the program at the project's bug tracker, asking for a change in the software's behaviour.
I'm trying to create a formset of related objects, where the relation itself is also an editable field.
Let's say we have a models.py like the one below.
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=45)
class Book(models.Model):
name = models.CharField(max_length=45)
author = models.ForeignKey(Author)
Is there a good, pythonic way to create a formset of all Books by a certain Author, and be able to change the Author of any of those books? This would be a particularly useful way to fix any errors in the foreign key relations.
My first instinct was that inlineformset would solve this for me, but I can't seem to get the Author of each book to be an editable field, even when I use custom forms.
using modelformset_factory will do the trick. (pulled from my comments here for completeness of the systems question/answer model)
I have two models and want to set a relation to them.
class ModelA(models.Model):
id = models.IntegerField(primary_key=True) # DB => PK, AI, NN
name = models.CharField(max_length=50)
...
class ModelB(models.Model):
modelA = models.OneToOneField("ModelA", primary_key=True)
description = models.CharField(max_length=255)
...
So I have a relationship between the two models. Is it possible to add a member to ModelA which stores the relation to ModelB without saving this relation to the database?
I would call it a dynamically created relation or something. Any hints oder suggestions how to let both models know each other?
I think it would be benefiting if the relation on one model can be done dynamically. Otherwise I'll get some trouble storing the models because one of the IDs won't be stored if I save one of the models.
I want to have the relation on both models so I can easily use the models as inline in django-admin.
regards
The reverse relation in Django is created by default.
To get the ModelA you will use:
ModelA.objects.filter(modelb__pk = 1)
You will find more details here:
https://docs.djangoproject.com/en/dev/topics/db/queries/
Django ORM will save ModelA first, then ModelB, in order to maintain data integrity in the DB.
Django can try saving multiple items in one transaction, and this way, if you cancel it, nothing will be saved, but this is possible in shell or in Python code. Over HTTP you can't maintain a transaction over several queries so far.
If you need to show model A as inline of model B, you need a custom admin interface, not new fields/models. I can't tell how to write custom admin widgets. I did do a similar thing with custom editor views & templates & Javascript. I stored the unsaved models in request.session.