I was developing a chat system with channels and have this models for a thread (some attributes removed for simplicity's sake):
class Thread(models.Model):
name = models.CharField(max_length=50, null=True, blank=True)
users = models.ManyToManyField('auth.User')
I realized it is also possible to implement it like this:
class Thread(models.Model):
name = models.CharField(max_length=50, null=True, blank=True)
class ThreadUsers(models.Model):
thread = models.ForeignKey(Thread, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
What are the advantages/disadvantages of using one over the other?
All what you do - is the same. For your last example with custom M2M through model you can add M2M declaration users in Thread:
class Thread(models.Model):
name = models.CharField(max_length=50, null=True, blank=True)
# M2M declaration, which use your ThreadUsers
users = models.ManyToManyField('auth.User', through='ThreadUsers' )
class ThreadUsers(models.Model):
thread = models.ForeignKey(Thread, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
Pros:
You create model self
you can change behavior your M2M connection manually.
You can add additional fields in M2M through connection.
You have full control of this model.
Cons:
problem with m2m connections in django admin.
too much additional code, you can get a hard-to-find errors.
some fields/widgets don't want to work with M2M.through.
all was happened is your problem, this is not tested. Auto-through relation is tested in box.
we have in our projects 50/50 M2M-autothrough vs M2M-manualthrough. if I want to have more control on the models and realations - i use custom through.
p.s. in M2M-autothrough case Django created SomethingLikeYourThreadUsers Model and Table automatically.
Related
I am having 3 different models - User, Thread and UserProfile.
User model contains information like ID, First_name and Last_name.
Thread model contains information like
class Thread(models.Model):
first_person = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name='thread_first_person')
second_person = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True,related_name='thread_second_person')
updated = models.DateTimeField(auto_now=True)
and UserProfile model,
class UserProfile(models.Model):
custom_user = models.OneToOneField(CustomUser, on_delete=models.CASCADE)
picture = models.ImageField(default='profile_image/pro.png', upload_to='profile_image', blank=True)
when I am trying to get all the threads from Thread model and pass it from views.py to my HTML template then I can access User model fields like -
{{ thread.second_person.ID}} {{ thread.second_person.First_name}}
But how can I access picture field from UserProfile with the help of custom_user ?
I'm a little confused: you say you have three models User, Thread and UserProfile. But inside your UserProfile model you reference a CustomUser model as the one-to-one relationship for the custom_user field. Then in your Thread model you reference a plain User model as the FKs for the first_person and second_person fields. Do you have 3 models or 4?
Assuming you only have 3 models, and that CustomUser is actually just User, then what you're trying to achieve should be doable. However you may need to change your conventions regarding related names to best practices in order to do so cleanly.
I have set up the models I think you need roughly below, and the code needed to access the relevant parts of each model within the template layer:
#models.py
class User(AbstractBaseUser):
# User Model Code
class Thread(models.Model):
first_person = models.ForeignKey(
User,
on_delete=models.CASCADE,
null=True,
blank=True,
related_name='thread_first_persons' # Note the Plural
)
# Related name for a FK is a one-to-many relationship
# (i.e. 1 User can be first_person on many threads)
# This may not be your desired behaviour, but it is possible on current set-up
second_person = models.ForeignKey(
User,
on_delete=models.CASCADE,
null=True,
blank=True,
related_name='thread_second_persons'
) # As above
updated = models.DateTimeField(auto_now=True)
class UserProfile(models.Model):
user = models.OneToOneField(
User,
on_delete=models.CASCADE
related_name='user_profile'
)
# Note: custom_user is confusing nomenclature.
# The above is best practice for User <> UserProfile 1-to-1 relationships
picture = models.ImageField(
default='profile_image/pro.png',
upload_to='profile_image',
blank=True
)
Now when you want access to the UserProfile from the Thread object, you can do so as follows inside a template:
{{ thread.second_person.user_profile.picture }}
Side Note: in your views.py file, if you are sending just the thread to your template, then to save your database several queries I would optimise with the following select_related parameters:
#views.py
threads = Thread.objects.select_related(
'first_person', 'first_person__user_profile',
'second_person', 'second_person__user_profile'
).all()
thread = Thread.objects.select_related(
'first_person', 'first_person__user_profile',
'second_person', 'second_person__user_profile'
).get(id=id)
We have two Django models:
class Project(models.Model):
project_title = models.CharField(max_length=30)
owner = models.ForeignKey(User, null=True, on_delete=models.DO_NOTHING)
class User(models.Model):
usernmae = models.CharField(max_length=50)
active_project = models.ForeignKey(User, null=True, on_delete=models.DO_NOTHING, related_name='current_project')
I have a user with object (with id say 692). And this user created a project with id=12345, therefore these owner field will get have this particular referenced.
I want to delete that user. But it shows error that
delete on table "app_user" violates foreign key constraint
This is expected as on_delete=models.DO_NOTHING, was set. One way I found out was using on_delete=models.CASCADE.
Question: How should I go about deleting the user (692) without changing the model definition(having to re-run migration)?
Doing it manually by deleting the project first, leads to the same foreign-key error, as owner field is User object.
How to handle this mutual foreign key relationship while deleting, as deleting any one of those two throws the foreign-key exception?
Update
Some correction in the model definition username is the field name instead of usernmae (typo). And the foreignkey for project is Project not the User model.
class Project(models.Model):
project_title = models.CharField(max_length=30)
owner = models.ForeignKey(User, null=True, on_delete=models.DO_NOTHING)
class User(models.Model):
username = models.CharField(max_length=50)
active_project = models.ForeignKey(Project, null=True, on_delete=models.DO_NOTHING, related_name='current_project')
IF you really don't want to make a migration (any specific reason?) and if you are ok with doing this manually this time. Then you have two options:
Go into the admin panel and manually change the User field in the project instance to a different user or to NULL. Now you should be able to delete the User instance since it's not referred anymore into the project.
If that worked, you can then delete the project instane as well.
Curios if this will work, let me know!
I am little bit comfused. Lets say I have such models.
models.py:
class Company(models.Model):
name = models.CharField(blank=False, null=False)
class Game(models.Model):
developer = models.ForeignKey(Company, on_delete=models.CASCADE)
publishers = models.ManyToManyField(Company)
If I use next code:
current_company = Company.object.get(pk=1)
current_company.game_set.all()
as I understand it return all games of current_company, but what field (developer or publishers) Django used?
But this code wouldn't be valid, for precisely this reason. If you tried to run it, Django would tell you that there was a conflict in the reverse relation.
If you have two relationships pointing to the same model, you need to explicitly set related_name on one of them to avoid this conflict.
I used inheritance model in my project after changing the model; but I give non-nullable field error. What should I do?
I am using Django 1.7
class Questions(models.Model):
question_category = models.ForeignKey(Course, blank=False)
question_author = models.ForeignKey(Author, blank=False)
question_details = models.CharField(max_length=100, blank=False, default='')
timestamp = models.DateTimeField(auto_now_add=True)
class TypeFive(Questions):
question_title = models.CharField(max_length=100, blank=False, default=generator(5), unique=True, editable=False)
def __str__(self):
return "{}".format(self.question_title)
class TypeFiveChoice(models.Model):
question_choice = models.ForeignKey(TypeFive)
is_it_question = models.BooleanField(default=False)
word = models.CharField(default='', blank=False, max_length=20)
translate = models.CharField(default='', blank=False, max_length=20)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return "{} : {}, {}".format(self.question_choice, self.word, self.translate)
After migrations:
You are trying to add a non-nullable field 'questions_ptr' to typefive without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows)
2) Quit, and let me add a default in models.py
In order to inherit from Questions in TypeFive, Django needs to add a relation from TypeFive to Questions. For all records in TypeFive that might already be in the database.
Django now doesn't know which question it should relate TopFive to. This is what the migrate command asks you for. You have a few options, but they greatly depend on your use case and whether you are in early development or if there is a production database where this migration has to run later.
I'm in early development and running it on localhost, so iI don't care
about my records. Now, what should I do?
In this case you haven't much to worry about, when migrate asks you type 1 and then press enter. Now add a primary key of a Questions instance that is in your database and then hit enter again.
Django now relates all TypeFive instances that are currently in the database to this question, so you might have to clean that up afterwards (e.g. by editing the TypeFive in Django admin).
#Nick Brady pointed this out in the question above so I don't mean to take credit but I wanted to highlight.
If your new inheritance class is only used for the purpose of being inherited from, you can easily get around this by setting your parent class to abstract.
class Parent(models.model):
Name = models.CharField(max_length=50)
class Meta:
abstract = True
class Child(Parent):
foobar = models.CharField(max_length=50)
class Meta:
db_table = "typenex_taxonomy_nodes"
I have a Django model class which encompass various types of model classes like:
Content > (Audio, Video, Image)
I want to perform queries on this parent model like Content.objects.filter() or Content.objects.recent(). How do i come about this? Presently i am using django's concrete model inheritance, which I suppose impose lot of overheard on the db performance by using joins for the parent classes. I cannot use abstract class because that would not permit me to perform queries on the parent class. Here are my models definitions:
class BaseContent(models.Model):
"""
Concrete base model to encompass all the local and social content.
We need a single queryset for all the content on the app.
"""
title = models.CharField(_('title'), max_length=255, default='')
description = models.TextField(_('description'), default='', blank=True)
publisher = models.ForeignKey(settings.AUTH_USER_MODEL)
allow_comments = models.BooleanField(default=False)
is_public = models.BooleanField(default=True)
created = AutoCreatedField(_('created'))
objects = BaseContentManager()
class Video(BaseContent):
ACTIVITY_ACTION = 'posted a video'
UPLOAD_TO = 'video_files/%Y/%m/%d'
PREVIEW_UPLOAD_TO = 'video_frames/%Y/%m/%d'
video_file = models.FileField(_('video file'), upload_to=UPLOAD_TO)
preview_frame = models.ImageField(
_('Preview image'), upload_to=PREVIEW_UPLOAD_TO, blank=True,
null=True)
category = models.ForeignKey(VideoCategory, blank=True, null=True)
num_plays = models.PositiveIntegerField(blank=True, default=0)
num_downloads = models.PositiveIntegerField(blank=True, default=0)
Thanks in advance.
I've had a similar scenario which i have solved using an external named Django Polymorphic, the library seemed to work seamlessly. A few major projects use Django Polymorphic including Django-shop.
Link:
https://github.com/chrisglass/django_polymorphic
Don't quote me on this but i've read a few sources in the past that have mentioned that django_polymorphic models does not have the same performance issues caused by the standard Django ORM concrete inheritance implementation.