Copy all fields of a django model instance - django

ok i think this is very basic, but since I am new to Django I don't know how to handle this.
I need to copy an instance of a django-model. As explained here, there is a problem with copying ManyToMany relations. But the attachment "django-model-copying.diff" has that function I guess. So I don't know - does my Django already have that function? I don't know how to call it.
Help would be appreciated.

The docs include directions on how to do this - including how to handle many to many relationships.

You can just do the following:
m = MyModel.objects.get(pk=1)
m.id = None
m.save()
That way new instance will be created with new id of course in case of any unique properties it will trigger errors during validation.
NOTE:
As for the function you've mentioned - it is not yet added to the trunk, the status is design decision needed, but if you know what you're doing you can manually apply the diff to your django instance - it's called patching btw. Here are some details about how to do it: http://ariejan.net/2007/07/03/how-to-create-and-apply-a-patch-with-subversion/.

I'll try to answer your actual problem, because what you are asking for in the question is a problem with your proposed solution, which as many have pointed out is not really ideal.
In the comments you mention this:
i'll try to explain.. So we have 2 models: User and Book. A User has a
book called "Titanic" with some content. Now, another user wants a
relation to that book too. But, the second user wants exactly the same
book, but it should be called "Ship is going under".. I would copy the
book, and rename it. - I know, i could also put the content of the
book in another model - but my model is a little bit more complex.
Looks like you have three things to track:
Users
Their Books
Some custom notes that users have about their "owned" book.
For each Book, store its common information, for example:
class Author(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField()
def __unicode__(self):
return unicode(self.name)
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ManyToMany(Author)
isbn = models.CharField(max_length=13)
# and so on
def __unicode__(self):
return unicode(self.title)
Now, you need to store Book -> User relation, such as one user can have many books, two users may own the same book, with their own meta information attached.
class BookClub(models.Model):
username = models.ForeignKey(User)
book = models.ForeignKey(Book)
comments = models.TextField()
If you structure your models like that, you'll be able to do queries like:
"How many books does each member have?"
"What is the most popular book?"
"What is the least popular book?"
"What is the most popular author?"

It's worth noting that as of django 1.8 you may have UUIDFields. If you copy a model instance and then save it the unique constraint will fail on this column, in which case you have to do something like:
import uuid
m = MyModel.objects.get(pk=1)
m.pk = None
m.uuid = uuid.uuid4() //generate new uuid
m.save()

Related

Database normalization in django

I need an optimally normalized database structure to achieve the following requirement.
models.py
class Learning_Institute(models.Model):
name = models.TextField()
user = models.ManyToManyField(settings.AUTH_USER_MODEL)
class Course(models.Model):
title = models.CharField(max_length=50)
instructor = models.ForeignKey(User, on_delete=models.PROTECT, related_name='courses_taught')
institute = models.ForeignKey(Learning_Institute, on_delete=models.PROTECT, related_name='courses')
I need the instructor field in the Course table to be limited to the set of users in Learning_Institute instead of all the users in the system.
How do I achieve this on the DB level?
I don't think that you can limit in the model itself.
One of the things that you can do is on form save to have validations using form clearing methods like so
And you can create a check that does something like this:
def clean_ instructor(self):
instructor = self.cleaned_data['instructor']
if instructor.type != "instructor":
raise forms.ValidationError("The user is not instructor!")
Another option is to create another User object that will inherit User and you can call it InstrcutorUsers
I have used this tutorial to extend the user model in django
I don't know if it's suitable for your scenario but changing the relations slightly may achieve what you want.
Removing the many to many for User and create a concrete association model for it, will
at least make sure the Course can only have users that also are instructors, by design.
Consider the following model structure:
class LearningInstitute(models.Model):
name = models.TextField()
class InstituteInstructor(models.Model):
class Meta:
unique_together=('user','institute')
user = models.ForeignKey(User, on_delete=models.PROTECT)
institute = models.ForeighKey(LearningInstitute, on_delete=models.PROTECT)
class Course(models.Model):
title = models.CharField(max_length=50)
instructor = models.ForeignKey(InstituteInstructor, on_delete=models.PROTECT)
You have LearningInstitutes
A user can be an instructor with a related institute, a User can only be related to the same institute once
A Course can only have an instructor (and by that also the related institute)
Design can easily be extended to let Courses have multiple instructors.
By design the Course can only have users that are also instructors.
There is a possibility in Django to achieve this in your model class. The option that can be used in models.ForeignKey is called limit_choices_to.
First I'd very strongly recommend to rename the field user in the class LearningInstitute to users. It is a many to many relation, which means an institute can have many users, and a user can perform some work in many institutes.
Naming it correctly in plural helps to better understand the business logic.
Then you can adapt the field instructor in the class Course:
instructor = models.ForeignKey(
'User', # or maybe settings.AUTH_USER_MODEL
on_delete=models.PROTECT,
related_name='courses_taught',
limit_choices_to=~models.Q(learning_institute_set=None)
)
This is not tested and probably will need some adjustment. The idea is to get all User objects, where the field learning_institute_set (default related name, since you haven't specified one) is not (the ~ sign negates the query) None.
This has however nothing to do with normalisation on the database level. The implementation is solely in the application code, and the database has no information about that.
As suggested by #TreantBG, a good approach would be to extend the class User and create class Instructor (or similar). This approach would affect the database by creating an appropriate table for Instructor.

Django - How to build an intermediate m2m model that fits? / best practice

First of all I have to admit that I'm quite new to all this coding stuff but as I couldn't find a proper solution doing it myself and learning to code is probably the best way.
Anyway, I'm trying to build an app to show different titleholders, championships and stuff like that. After reading the Django documentation I figured out I have to use intermediate models as a better way. My old models.py looks like this:
class Person(models.Model):
first_name = models.CharField(max_length=64)
last_name = models.CharField(max_length=64)
[...]
class Team(models.Model):
name = models.CharField(max_length=64)
team_member_one = models.ForeignKey(Person)
team_member_two = models.ForeignKey(Person)
class Championship(models.Model):
name = models.CharField(max_length=128)
status = models.BooleanField(default=True)
class Titleholder(models.Model):
championship = models.ForeignKey(Championship)
date_won = models.DateField(null=True,blank=True)
date_lost = models.DateField(null=True,blank=True)
titleholder_one = models.ForeignKey(Person,related_name='titleholder_one',null=True,blank=True)
titleholder_two = models.ForeignKey(Person,related_name='titleholder_two',null=True,blank=True)
Championships can be won by either individuals or teams, depending if it's a singles or team championship, that's why I had to foreign keys in the Titleholder class. Looking at the Django documentation this just seems false. On the other hand, for me as a beginner, the intermediate model in the example just doesn't seem to fit my model in any way.
Long story short: can anyone point me in the right direction on how to build the model the right way? Note: this is merely a question on how to build the models and displaying it in the Django admin, I don't even talk about building the templates as of now.
Help would be greatly appreciated! Thanks in advance guys.
So I will take it up from scratch. You seem to be somewhat new to E-R Database Modelling. If I was trying to do what you do, I would create my models the following way.
Firstly, Team would've been my "corner" model (I use this term to mean models that do not have any FK fields), and then Person model would come into play as follows.
class Team(models.Model):
name = models.CharField(max_length=64)
class Person(models.Model):
first_name = models.CharField(max_length=64)
last_name = models.CharField(max_length=64)
team = models.ForeignKey(to=Team, null=True, blank=True, related_name='members')
This effectively makes the models scalable, and even if you are never going to have more than two people in a team, this is good practice.
Next comes the Championship model. I would connect this model directly with the Person model as a many-to-many relationship with a 'through' model as follows.
class Championship(models.Model):
name = models.CharField(max_length=64)
status = models.BooleanField(default=False) # This is not a great name for a field. I think should be more meaningful.
winners = models.ManyToManyField(to=Person, related_name='championships', through='Title')
class Title(models.Model):
championship = models.ForeignKey(to=Championship, related_name='titles')
winner = models.ForeignKey(to=Person, related_name='titles')
date = models.DateField(null=True, blank=True)
This is just the way I would've done it, based on what I understood. I am sure I did not understand everything that you're trying to do. As my understanding changes, I might modify these models to suit my need.
Another approach that can be taken is by using a GenericForeignKey field to create a field that could be a FK to either the Team model or the Person model. Or another thing that can be changed could be you adding another model to hold details of each time a championship has been held. There are many ways to go about it, and no one correct way.
Let me know if you have any questions, or anything I haven't dealt with. I will try and modify the answer as per the need.

django m2m_changed with custom through model

In Django I do have two models "Author" and "Publication" that are connected with a Many-to-Many-Field, so that I can assign different authors to a publication. Additionally, I have to use a custom through-model "Authorship" to define the correct order.
class Author(models.Model):
first_name = models.CharField(max_length=48)
.....
class Authorship(models.Model):
author = models.ForeignKey(Author)
publication = models.ForeignKey('Publication')
order_of_authorship = models.IntegerField(default=1)
class Publication(models.Model):
title = models.CharField(max_length=128)
authors = models.ManyToManyField(Author, through=Authorship)
year = models.IntegerField(max_length=4)
...
citation_key = models.CharField(max_length=9, blank=True, default="")
At the moment I use the Admin Interface to populate my data with a form for the "Publication" and an inline form "Authorship".
What I want to achieve now:
An additional citation_key-field (e.g. "Einstein1950") should be auto-populated after data has changed.
What I tried to do:
I found out that using signals must be the best practice.
However the "m2m_changed"-Signal on "Publication.authors.through" is not fired, when I change the Authorships.
#receiver(m2m_changed, sender=Publication.authors.through)
def authors_changed(sender, **kwargs):
print("authors changed")
This problem is also discussed in a related topic, where the author seems to use "post_save" on the through-model.
#receiver(post_save, sender=Authorship)
def authorship_changed(sender, instance, **kwargs):
print("authors changed")
This seems to work out, but I have to keep in mind, that a deletion is not covered yet, so I added a post_delete-signal:
#receiver(post_delete, sender=Authorship)
def authorship_deleted(sender, instance, **kwargs):
print("authors deleted")
The problem now is: If I add 4 authors, I get that event fired 4 times. If I want to update my citation_key as described before, this happens also 4 times.
Can this be the correct solution? Or is there a better best practice? I assume it must work somehow with the m2m_changed signal, but I don't know how.
Since I am new to Django, I don't know if this is the obvious solution for you. Furthermore, in this scenario the unnecessary calculation should not have a huge impact, but it is not nice at all.
I only found a really old bug-report in the Django-Trac that seems to address this problem as well. But there is not solution yet.
This is a known bug, reported as ticket 17688 on Django.

Django ORM for mutual follow query

user_id user_follow
3 2
3 2
3 2
2 3
login user id=3, now i want to get people who follow me and i follow them, mutual follow in django framework for login user. above senario show login user(id=3) follow the user(id=2)
and user(id=2) also follow the login user(id=3) now I want to show to login user, how many user follow you and you follow him. using django orm
class Cause(models.Model):
description = models.TextField()
start_date = models.DateTimeField(null=True, blank=True)
creation_date = models.DateTimeField(default=datetime.now)
creator = models.ForeignKey(User, related_name='cause_creator_set')
attendees = models.ManyToManyField(User)
class Attendance(models.Model):
user = models.ForeignKey(User)
cause = models.ForeignKey(Cause)
user_follow = models.IntegerField(max_length=255)
registration_date = models.DateTimeField(default=datetime.now)
follow = models.BooleanField(default=False)
def __unicode__(self):
return "%s is attending %s" % (self.user.username, self.event)
class Meta(object):
verbose_name_plural = "Attendance"
There are multiple issues here, I'm going to try and address them all.
The relationship your models describe is what called a Extra field on ManyToMany relationship. Simply put, Cause has a ManyToMany to User, with Attendance being an intermediatry table. The benefits of using the through instruction for this are mostly for ease-of-access, and you can read the documentation I linked for examples. So:
attendees = models.ManyToManyField(User, through='Attendance')
Now let's talk about Attendance. I have a problem with two fields:
user = models.ForeignKey(User)
user_follow = models.IntegerField(max_length=255)
follow = models.BooleanField(default=False)
Yes, I showed 3 because only 2 of them are problematic. So you have a ForeignKey to User, right? But what is a ForeignKey? It's simply an IntegerField that points on the row (pk) on a different table. There are other stuff that happens here, but the basic thing is it's just an integerfield. And then you wanted to link that model to user again, right? So instead of adding another ForeignKey, you used an IntegerField.
Do you understand why that's a bad thing? I'll explain - you can't use the ORM on your own logic. Now the basic thing I'm assuming you are trying to accomplish here is actually this:
# let's pretend that User isn't built-in
class User(models.Model):
follow = models.ManyToManyField('self')
Then there's the follow Boolean which I really don't get - if a user is linked to another one, doesn't that automatically mean he's following him? seems more like a method than anything.
In anyway, I can think of two very valid options:
Use another ForeignKey. Really, if that what makes sense, there's nothing wrong with multiple ForeignKeys. In fact, it will create a sort of ManyToMany from user to himself going through Attendance as an intermediary table. I think this makes sense
Create your own User class. Not a terribly complicate feat. I also recommend this, since it seems you have plenty of customization. The built in User is great because it comes pretty much out of the box. But at the same time, the built-in user is pretty much built around the admin interface and kinda limits you a lot. A custom one would be easier. Then you can do the recursive relationship that seems to be what you're looking for, then implament is_following as a boolean method (seems more appropriate).
The last thing I'm gonna do is show you the method you're looking for. I'm showing you how to do it though as I mentioned, I strongly recommend you first take my suggestion and build your own User model. The method would look different of course, but it wouldn't be hard to convert.
That being said, the key here is simply creating two methods - one for checking if a user is related. A second one for checking if you're being followed by him. So if I understand you correctly, you're trying to achieve:
#using your way
def follows(a, b):
if Attendence.objects.filter(user__pk=a, user_follow=b).count() > 0:
return True
return False
def mutual(a, b):
if follows(a, b) and follows(b, a):
return True
return False
a, b = 2, 3
mutual(2, 3) # will output True
#using a custom User Class:
class MyUser(AbstractBaseUser):
# required fields here...
follow = models.ManyToManyField('self')
def is_following(self, user):
if user in self.follow.all():
return True
return False
def mutual(self, user):
if self.is_following(user) and user.is_following(self):
return True
return False

Displaying a sorted list with ManyToMany relation

I have a certain number ob Subjects and Users. An user can vote for one or more subjects. I'd like to make a view displaying the top 10 Subjects sorted by count of votes in descanding order.
In fact, I found a working way, but I'm asking myself if there is more suitable way to do that.
I started with models:
class Subject(models.Model):
text = models.TextField()
class User(models.Model):
name = models.CharField(max_length=64)
subject = models.ManyToManyField(Subject)
now I can type:
Subject.user_set.all()
and I will get those users who has voted for the subject.
Now I would like to make a list view, where top 10 subjects would be displayed sorted by the number of votes.
So I added a class method to the Subject class:
class Subject(models.Model):
text = models.TextField()
#staticmethod
def by_votes():
myList = list(Subject.objects.all())
return sorted(myList, key=lambda s: s.user_set.count(), reverse=True)[:10]
and defined that class based view:
class SubjectListView(ListView):
model = Subject
template_name = "subject_list.html"
context_object_name = "subject_list"
queryset = Subject.by_votes()
Which actually works well.
But I saw in the docs Manager and QuerySet classes, unfortunately I did not quite understand how to use them (defining custom ones) to get what I'm looking for (without dealing with raw SQL queries).
I'm little bit afraid, because I used a list I could run into memory problems having large number of subjects.
What would you say, would it be more suitable to use custom Manager oder QuerySet for that, wouldn't?
If yes, how to do it?
Any other alternative ideas?
Thanks!
Peter
No need for custom managers or raw SQL here, just aggregation.
from django.db.models import Count
Subject.objects.annotate(user_count=Count('user').order_by('-user_count')[:10]
I'm fairly new to this but I solved a similar problem with this:
Subject.objects.all().annotate(count=Count('user')).order_by('-count')[:10]
Aggregation Doc