Django ManyToManyField reverse Relationship - django

I would like to do a reverse relationship on my table Tickets.
Here is my model :
class Tickets(models.Model):
ticket_title = models.CharField(max_length=100)
ticket_content = models.TextField()
class User_Detail(models.Model):
user = models.OneToOneField(User)
tickets = models.ManyToManyField(Tickets, blank=True, null=True)
I create my ticket like that :
ticket = Tickets.objects.create(ticket_title="test", ticket_content="test content")
request.user.user_detail.tickets.add(ticket)
and the thing I'm having an issue to do is to get the username of the guy who post the ticket, (without request.user)
so I tried like that :
ticket = Tickets.objects.get(pk=1)
ticket.user_detail_set.user.username
but I get
AttributeError: 'ManyRelatedManager' object has no attribute 'user'
Thanks you for watching, I hope you'll understand.

Since you set up a many-to-many relationship, a Ticket may have many User_Detail objects. Therefore, Ticket.user_detail_set is a manager, not a single object. You could get the first user associated with a Ticket like this:
ticket.user_detail_set.first().user.username
But it sounds like you actually want a one-to-many relationship between Ticket and User_Detail, meaning you actually want Ticket to have a foreign key relationship. Your models should probably look like this:
class User_Detail(models.Model):
user = models.OneToOneField(User)
class Ticket(models.Model):
user = models.ForeignKey(User)
title = models.CharField(max_length=100)
contents = models.TextField()
Then you can do:
ticket = Ticket.objects.get(pk=1)
user = ticket.user
You might even be able to drop the User_Detail model entirely, unless you use it elsewhere in your application and/or it has more fields than what is shown here.

Related

Django Graphene hiding a model field (or return null) based on user preference in ManyToMany

assume we have the following Django Models:
class Person(models.Model):
name = models.CharField(max_length=255)
personal_field = models.CharField(max_length=255)
class Group(models.Model):
name = models.CharField(max_length=255)
participants = models.ManyToManyField(
"Person",
through="GroupPersonOption",
through_fields=('group', 'person')
)
class GroupPersonOption(models.Model):
person = models.ForeignKey("Person", on_delete=models.CASCADE)
group = models.ForeignKey("Group", on_delete=models.CASCADE)
hide_personal_field = models.BooleanField(default=False)
Example:
Based on the group a user is in, they can choose to hide their personal_field, so that instead of its regular value, None or Null is returned.
The Person can set this in the ManyToMany Model called GroupPersonOption
Where it gets tricky:
How do I change its value to None in my DjangoObjectTypes?
When I try to add a resolve function for the personal_field in PersonObjectType, I don't know if the user wanted to hide this info.
Graphene Code:
{
peopleByGroup(name: "DjangoIsAwesome") {
person {
name
personal_field
}
}
}
I have waited a few days in the Django Subreddit but the only solution one person came up with was just to ignore setting the personal_field to None based on some value and then hide it in the frontend, however that is not an acceptable solution, as this would expose user data.
I have also tried adding a resolve_person function to the PersonEventOptionType, but that broke a lot of things, as I could not find an example of resolving foreign keys in a ManyToMany DjangoOjectType.
Any help is appreciated.
Thanks for your time.

Django ListView Through Model Relationships

I have been reading through here and I can't seem to find the answer of the question I am looking for. Maybe I'm not asking the correct question.
I have three models.
Recruiter
Office
Recruit
Recruiters are assigned to multiple offices and each office has multiple employees. What I need to be able to do is create a listview that lists all of the employees that are associated with a recruiter. So something like this:
Recruiter 1 has Office 1 and 2 assigned to them. Office 1 has Employee 1,2,3. Office 2 has employee 3,4,5
The Listview should display all employees under Recruiter 1.
My Models are:
class Recruit(models.Model):
id = models.CharField(max_length=25,primary_key=True, unique=True)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
mobile_phone = models.CharField(max_length=20,null=True)
preferred_phone = models.CharField(max_length=20,null=True)
email= models.CharField(max_length=100,null=True)
officeid = models.ForeignKey(Office,on_delete=models.DO_NOTHING,related_name="recruit")
class Office(models.Model):
office_name = models.CharField(max_length=100)
officeid = models.CharField(max_length=20,primary_key=True,unique=True)
class Recruiter(models.Model):
user = models.OneToOneField(User, related_name='recruiter',on_delete=models.CASCADE)
organization = models.ForeignKey(UserProfile,on_delete=models.CASCADE)
Do I need a AssingedOffices table to join this all together?
class AssignedOffice(models.Model):
user = models.ForeignKey(UserProfile,on_delete=models.CASCADE)
esa_office = models.ForeignKey(Office,on_delete=models.CASCADE)
Just not sure how to connect them & display in the listview.
Edit Added start of view.
class MyESA(LoginRequiredMixin,ListView):
template_name = "recruits/recruits_list.html"
user= User.objects.select_related('recruiter')
paginate_by = 20
model=Recruit
context_object_name = 'recruits'
def get_queryset(self):
queryset = Recruit.objects.filter(probablity=1)
return queryset
If you want to achieve a Many-to-one relationship like this
Recruiters are assigned to multiple offices
then you should have a ForeignKey in the Office model not the other way around. I presume that each Office has one Recruiter. Otherwise you should have a Many-to-Many relationship that is constructed with a ManyToManyField
I presume that by
each office has multiple employees
You meant that each Office object has multiple Recruit objects
If you want to list all of the Recruit objects in a ListView for model Recruiter then I think the easiest way would be to add a ForeignKey field in the Recruit model and refer to it via related object reference

Django query using related_name through another related_name

I have several models that have a ForeignKey back to a model which has a ForeignKey back to auth User in Django.
models.py
class UserDetails(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
related_name='userdetail_related')
details = models.CharField(max_length=100)
email = models.EmailField(unique=True)
class UserInformation(models.Model):
user = models.ForeignKey(
UserDetails,
related_name='userinfo_related')
info = models.CharField(max_length=100, default='this')
EDIT
My actual code for related_name as per Django Documentation is: related_name='%(app_label)s_%(class)s_related'. I put 'userdetail_related' for ease of explanation here.
Only one UserDetail per User, but many UserInformation per UserDetail.
Where there is an unregistered user and we have captured their email, the email can have UserDetail and UserInformation associated with it for a shopping cart guest checkout system.
In my View I want to access the UserInformation model from self.request.user.
I can access UserDetails in my view via:
details = self.request.user.userdetail_related.filter(
user=self.request.user).first()
But I can't seem to access UserInformation via:
info = self.request.user.userdetail_related.filter(
user=self.request.user).first().userinfo_related.filter(
info='this').first()
The only way I can get this to work is:
details = self.request.user.userdetail_related.filter(
user=self.request.user).first()
info = details.userinfo_related.filter(
info='this').first()
But this surely hits the database twice which I don't want.
Does anyone have a better way of getting the info from UserInformation using the session user 'through' UserDetails?
You can use following:
user_info = UserInformation.objects.filter(user__user=self.request.user).first()
Additionally, when you access UserDetails you don't really need the filter since you are trying to access the related objects from the user itself. So following would work as well.
details = self.request.user.userdetail_related.first()
And as a side note, I think you need OneToOneField here since one user should have only one UserDetails.
As it suggested by #AKS you should use OneToOneField to connect your models to the User. You can do it like this:
class UserDetails(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
related_name='userdetail_related')
details = models.CharField(max_length=100)
class UserInformation(models.Model):
user = models.OneToOneField(
UserDetails,
related_name='userinfo_related')
info = models.CharField(max_length=100, default='this')
Then you can access UserInformation and UserDetails like this:
details = self.request.user.userdetail_related.details
info = self.request.user.userinfo_related.info
To add to the other answers, a few remarks about the layout.
For Model names, best to always use singular (UserDetails -> UserDetail), not plural. Then, for the related name of a ForeignKey, use the plural (because the reverse lookup may find more than one item that have the backwards relationship).
class UserDetail(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='details')
details = models.CharField(max_length=100)
class UserInformation(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='info')
info = models.CharField(max_length=100, default='this')
Makes it much simpler to access in the views
request.user.infos.all().first()
request.user.details.filter(info__startswith="something")
Also, if practical, add both onto the User object, because "flat is better than nested" .
If every User only has one UserDetail and one UserInformation then its best to use OneToOneFields instead.

django manytomany through

If I have two Models that have a manytomany relationship with a through model, how do I get data from that 'through' table.
class Bike(models.Model):
nickname = models.CharField(max_length=40)
users = models.ManyToManyField(User, through='bike.BikeUser')
The BikeUser class
class BikeUser(models.Model):
bike = models.ForeignKey(Bike)
user = models.ForeignKey(User)
comment = models.CharField(max_length=140)
And I would add a user to that bike (presuming I have a myBike and a myUser already)
BikeUser.objects.create(bike = myBike, user = myUser, comment = 'Got this one at a fancy store')
I can get all the users on 'myBike' with myBike.users.all() but how do I get the 'comment' property?
I would like to do something like
for myBikeUser in myBike.users.all():
print myBikeUser.comment
The through table is linked by standard ForeignKeys, so you do a normal ForeignKey lookup. Don't forget that there's a comment for each bikeuser, ie one for each bike/user pairing.
for myBikeUser in myBike.bikeuser_set.all():
print myBikeUser.comment, myBikeUser.user.first_name

How do you set the initial value for a ManyToMany field in django?

I am using a ModelForm to create a form, and I have gotten the initial values set for every field in the form except for the one that is a ManyToMany field.
I understand that I need to give it a list, but I can't get it to work. My code in my view right now is:
userProfile = request.user.get_profile()
employer = userProfile.employer
bar_memberships = userProfile.barmembership.all()
profileForm = ProfileForm(
initial = {'employer': employer, 'barmembership' : bar_memberships})
But that doesn't work. Am I missing something here?
Per request in the comments, here's the relevant parts of my model:
# a class where bar memberships are held and handled.
class BarMembership(models.Model):
barMembershipUUID = models.AutoField("a unique ID for each bar membership",
primary_key=True)
barMembership = USStateField("the two letter state abbreviation of a bar membership")
def __unicode__(self):
return self.get_barMembership_display()
class Meta:
verbose_name = "bar membership"
db_table = "BarMembership"
ordering = ["barMembership"]
And the user profile that's being extended:
# a class to extend the User class with the fields we need.
class UserProfile(models.Model):
userProfileUUID = models.AutoField("a unique ID for each user profile",
primary_key=True)
user = models.ForeignKey(User,
verbose_name="the user this model extends",
unique=True)
employer = models.CharField("the user's employer",
max_length=100,
blank=True)
barmembership = models.ManyToManyField(BarMembership,
verbose_name="the bar memberships held by the user",
blank=True,
null=True)
Hope this helps.
OK, I finally figured this out. Good lord, sometimes the solutions are way too easy.
I need to be doing:
profileForm = ProfileForm(instance = userProfile)
I made that change, and now everything works.
Although the answer by mlissner might work in some cases, I do not think it is what you want. The keyword "instance" is meant for updating an existing record.
Referring to your attempt to use the keyword "initial", just change the line to:
bar_memberships = userProfile.barmembership.all().values_list('pk', flat=True)
I have not tested this with your code, but I use something similar in my code and it works.