Django intermediary model - python

I dont understand Django Intermediary models at all. Let's say that i want to add one more additional field to User model, and store relationship between two users in another model(intermediary) like this:
class Contact(models.Model):
user_from = models.ForeignKey(User...)
user_to = models.ForeignKey(User...)
...
And the field i want to add is:
following = models.ManyToManyField('self',
through=Contact,
related_name='followers',
symetrical=False)
What really happen when i call user_object.followers.all() and what is the difference between user_object.following.all() are they both the same?

Related

How to allow boolean True on just one model in table in Django?

I've got a model where I would like there to be able to have one set as the 'app default'.
In this model I added a field named is_app_default in order to help accommodate this.
class TreeLevel(models.Model):
id = models.BigAutoField(primary_key=True)
short_description = models.CharField(max_length=200)
long_description = models.TextField()
is_app_default = models.BooleanField(default=False)
class Meta:
verbose_name = "Tree Level"
verbose_name_plural = "Tree Levels"
class Layer(models.Model):
id = models.BigAutoField(primary_key=True)
tree_levels = models.ManyToManyField(TreeLevel)
description = models.TextField()
class Meta:
verbose_name = "Layer"
verbose_name_plural = "Layers"
The Layer model links to TreeLevel with an m2m. Ultimately I would like the is_app_default TreeLevel automatically added to every Layer m2m relationship - which is why there can only be one TreeLevel with is_app_default set as True.
My potential solution(s):
Users with admin may be creating new TreeLevel objects - so I need to make sure they aren't setting that Boolean in any new models. I think I can override the save() method on TreeLevel to check the DB to see if another with that boolean as True exists - if so? Don't save the 'new' one and return an error. But I think this hits the database - causing unnecessary queries potentially?
Then additionally, I would also need to override the save() method on Layer and add the 'default' TreeLevel to that many2many at that time.
Is there a good way to 'automatically' handle this with these relationships?
My ultimate goal:
Have a default TreeLevel that is automatically added to every Layer many to many relationship whenever a Layer is created.
Questions:
Would my proposed solution work?
I've tried looking into Djangos Unique Constraints, but I think those are more on a row level, as opposed to a Table level.

What fields can you call with request.user in Django?

Sorry for the dumb question but I can't figure it out.
I have a class based view for a form. I like to make some changes if the request.user is equal with something.
I used before in some other views request.user.profile.leader that gives me a boolean answer. Thats OK.
Now in this class based view I like to use a very similar stuff but with another model like this: request.user.trust.group but it gives me nothing. What am I doing wrong?
If you haven't customized your user model, then profile will appear on it as a reverse descriptor of an one-to-one field on another model (by way of related_name having been set or inferred), i.e. you have something like
class Profile(Model):
user = models.OneToOneField(User, related_name="profile")
leader = models.BooleanField(...)
somewhere.
If you expect a trust field to be there, then you'd need something similar:
class Trust(Model):
user = models.OneToOneField(User, related_name="trust")
group = ...
On the other hand, if you do have an entirely custom user model, then those attributes could appear directly on it:
class CustomUser(AbstractBaseUser):
profile = models.ForeignKey(Profile, ...)
trust = models.ForeignKey(Trust, ...)

Django queryset has no attribute when using select related

I'm trying to using select_related to queryset, and it returns queryset has no attribute when using select related. I made two models, and one model has foreignkey column, it is 1:1.
models
class User(models.Model):
name = Charfield()
class Item(models.Model):
user = models.ForegnKey(User, on_delete=models.CASCADE, related_name='user_item_set', null=True)
When I try this queryset, it says queryset does not have select related.
users = User.objects.get(id=pk).select_related('user_item_set')
Looks like you misunderstood the usage of select_related().
Returns a QuerySet that will “follow” foreign-key relationships, selecting additional related-object data when it executes its query.
It can be used on Item model (the model which defines the ForegnKey field) and not User model.
What you need to use is prefetch_related(). Something like this:
users = User.objects.get(id=pk).prefetch_related('item_set')

Multiple User type with Django

Using Django we have two types of Users (Teachers and Students) with common fields and uncommon fields
In our wizard we first POST the common fields to /users with an extra type_field
Every operation after this should be able to figure out which model (Teacher or Student) it needs to use.
We are thinking of making two models ( Teacher and Student ) with an one-to-one field.
But how do we hookup the type_field to the right Model on every operation?
You dont have to go for an extra field since you are already having two different classes for students and teachers. A simple approach may looks like below.
from django.contrib.auth.models import User
class Teacher(User):
extra_field_1 = models.Fieldtype()
...
...
class Student(User):
extra_field_1 = models.Fieldtype()
...
...
You can provide both type of users same registration form and upon clicking next take them to next page based on the value of I am a teacher/student field. In that case I suggest you to use atomic blocks if you dont want to save data in case registration procedure fails at some point or user have selected a wrong choice and they want to go back. By this approach each inherited models have username, first_name, last_name and email that you dont have to insert any of these to Teacher or student model.
Then you have to create forms for each model. You may use modelform A much better approach will be using class based views since that reduce a lot of code and stick to dry principles.
You may use something like:
class Person(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
# more common fields
class Student(Person):
specific_field_to_student = models.CharField(max_length=255)
# more fields
class Teacher(Person):
specific_field_to_teacher = models.CharField(max_length=255)
# more fields
In your database you will have 3 tables (yourapp_person, yourapp_student and yourapp_teacher). Now, if type_field value is student, you will use Student model to create user, if it is teacher, you will use Teacher model.
Note: You may need to make Person model above a subclass of the built-in User model.
Edit:
I have edited the model above to take into account the requirements in the comments below.
Now, to retrieve user by id, you can use the following code in your view:
user = Person.objects.get(id=id) # id is the view param
if hasattr(user, 'student'):
print("Student")
else: # hasattr(user, 'teacher')
print("Teacher")

How do you force only one relationship in django when multiple are possible?

I am creating a web application to manage robotics teams for our area. In the application I have a django model that looks like this:
class TeamFormNote(models.Model):
team = models.ForeignKey(Team, blank=True, null=True)
member = models.ForeignKey(TeamMember, blank=True, null=True)
notes = models.TextField()
def __unicode__(self):
if self.team:
return "Team Form Record: " + unicode(self.team)
if self.member:
return "Member Form Record: " + unicode(self.member)
Essentially, I want it to have a relationship with team or a relationship with member, but not both. Is there a way to enforce this?
I can only see two viable solutions. First is actually the same as #mariodev suggested in the comment which is to use Genetic foreign key. That will look something like:
# make sure to change the app name
ALLOWED_RELATIONSHIPS = models.Q(app_label = 'app_name', model = 'team') | models.Q(app_label = 'app_name', model = 'teammember')
class TeamFormNote(models.Model):
content_type = models.ForeignKey(ContentType, limit_choices_to=ALLOWED_RELATIONSHIPS)
relation_id = models.PositiveIntegerField()
relation = generic.GenericForeignKey('content_type', 'relation_id')
What that does is it sets up a generic foreign key which will allow you to link to any other model within your project. Since it can link to any other model, to restrict it to only the models you need, I use the limit_choices_to parameter of the ForeignKey. This will solve your problem since there is only one generic foreign key hence there is no way multiple relationships will be created. The disadvantage is that you cannot easily apply joins to generic foreign keys so you will not be able to do things like:
Team.objects.filter(teamformnote_set__notes__contains='foo')
The second approach is to leave the model as it and manually go into the database backend and add a db constaint. For example in postgres:
ALTER TABLE foo ADD CONSTRAINT bar CHECK ...;
This will work however it will not be transparent to your code.
This sounds like a malformed object model under the hood...
How about an abstract class which defines all common elements and two dreived classes, one for team and one for member?
If you are running into trouble with this because you want to have both referenced in the same table, you can use Generic Relations.

Categories