Django ORM Confusion with Query - django

I have customer Model
Name, Email, Phone, State
example
A, Aemail, Apohone, Astate
A, Bemail, Apohone, Astate
A, Cemail, Apohone, Astate
Means one customer with multiple emails.
Here what i want to get out of the query.
[{A, Aphone, Astate, [Aemail, Bemail, Cemail]},
{....},
]
Tried
Customers.objects.filter(**params).values_list('phone', 'name').distinct()
Thougts?

The schema you describe is sub-optimal. Using this schema will result in consistency issues when only some of the user's data will be updated/deleted.
If you want the Customer to have multiple emails if would be wiser to create a separate Email model or use array field.
class Customer(models.Model):
name = models.CharField(max_length=50)
class CustomerEmail(models.Model):
customer = models.ForeignKey(Customer, related_name='emails')
email = models.EmailField()
Now it becomes easy make the query you want.
customers = Customer.objects.select_related('emails')
And get their emails:
for email in customers.emails:
print(email.email)

Related

Django ORM verification of a row in the database

In my site, the buyer search for a product and once he found it he can contact the seller by pressing on a contact button. If the conversation between the two concerning this product exists he should be redirected to the existing conversation, else, we create a new conversation.
The conversation is hence defined by two users and a listing.
When I try to implement the logic, I am not able to verify both conditions of the existance of the conversation, if the listing exists OR the users exists Django returns that the conversation exists. Here is my code:
def create_conversation(request, user_pk1, user_pk2, results_pk):
user1 = get_object_or_404(get_user_model(), pk=user_pk1)
user2 = get_object_or_404(get_user_model(), pk=user_pk2)
results= get_object_or_404(Listing, pk=results_pk)
existing_conversation = Conversation.objects.filter(users__in=[user1, user2]).filter(listing=results).values_list('id', flat=True)
if existing_conversation.exists():
return HttpResponseRedirect(reverse('conversation:conversation_update', kwargs={'pk':existing_conversation[0]}))
else:
conv=Conversation()
conv.save()
conv.listing = results
conv.save()
conv.users.add(*[user1,user2])
return HttpResponseRedirect(reverse('conversation:conversation_update', kwargs={'pk': conv.pk}))
Here is the model of the conversation:
class Conversation(models.Model):
"""
Model to contain different messages between one or more users.
:users: Users participating in this conversation.
:archived_by: List of participants, who archived this conversation.
:notified: List of participants, who have received an email notification.
:unread_by: List of participants, who haven't read this conversation.]\['
listing: the listing the conversation is about.
read_by_all: Date all participants have marked this conversation as read.
"""
users = models.ManyToManyField(
settings.AUTH_USER_MODEL,
verbose_name=_('Users'),
related_name='conversations',
)
# review the listing before going online, because it is necessary to have a conversation listing
listing = models.ForeignKey (
Listing,
verbose_name=_('Listing'),
related_name='listing',
default= 1,
)
and the model of the listing:
class Listing(models.Model):
seller = models.ForeignKey(Profile, related_name='seller_listing', verbose_name='sellr', limit_choices_to={'is_seller': True})
location_Country = models.CharField(max_length=45, verbose_name=_('from_Country'))
location_State = models.CharField(max_length=45, verbose_name=_('from_State'), null=True, blank=True)
location_City = models.CharField(max_length=45, verbose_name=_('from_City'))
I also tried an approach of divinding it into two conditions: a = conversation.objects.filter(users) and b= conversation.objects.filter(listing), then use if a and b then the conversation exists but got the same issue.
and existing_conversation = Conversation.objects.filter(Q(users__in=[user1, user2]) & Q(listing=results)).values_list('id', flat=True) but got the same issue. Thank you in advance for your help.
You can use intersection() method of django, added since Django 1.11, operator to return the shared elements of two or more QuerySets or the bitwise operation AND used with the sign `& to get the same behavior.
So in your case, check whether there's an intersection between the two users with & or intersection()
existing_conversation = (user1.conversations.all() & user2.conversations.all()).filter(listing=results)
# or with django intersection
existing_conversation = (user1.conversations.all().intersection(user2.conversations.all())).filter(listing=results)
if existing_conversation.exists():
return HttpResponseRedirect(reverse('conversation:conversation_update',
kwargs={'pk':existing_conversation.first().pk}))
else:
# rest of the code
BONUS, I see a typo in your code, you didn't send the pk as argument:
kwargs={'pk':existing_conversation[0]}
get the first instance with first() and get the pk
kwargs={'pk':existing_conversation.first().pk}
or
kwargs={'pk':existing_conversation[0].pk}

how to store commaseperated values in one column ,if user selected morethan one choice from form

I have "user" Model
username email phonenumber
I want to access all "user" model emails in other model "B" and make user to select more than one email and store it has commaseperatedvalues
"B" colums are
organization Designation share_knowledge_with
abc manager (here all emails which user selected
to be stored with commaseperated)
I tried like this but not working:
MODEL
class B(models.Model):
organization=models.CharField(max_length=200,blank=False,null=True)
emails_for_help = models.TextField(null=True,help_text="select with whom
you want to share knowledge")
form
class Bform(ModelForm):
emails_for_help=forms.ModelMultipleChoiceField(queryset=User.objects.all(),
widget=forms.CheckboxSelectMultiple)
class Meta:
model=B
fields=["organization","emails_for_help"]
I tried like this but it is taking null value in "emails_for_help"
emails_for_help = models.TextField(null=True,help_text="select with whom
you want to share knowledge")
Change this to
emails_for_help = models.TextField(null=False,help_text="select with whom
you want to share knowledge")
to refuse null values. It will throw an exception you will have to handle, and in that exception you could display a message to the user that no selections aren't permitted.

Django query of table with implicit join on itself

I've read the documentation and looked at other questions posted here, but I can't find or figure out whether this is possible in Django.
I have a model relating actors and movies:
class Role(models.Model):
title_id = models.CharField('Title ID', max_length=20, db_index=True)
name_id = models.CharField('Name ID', max_length=20, db_index=True)
role = models.CharField('Role', max_length=300, default='?')
This is a single table that has pairs of actors and movies, so given a movie (title_id), there's a row for each actor in that movie. Similarly, given an actor (name_id), there's a row for every movie that actor was in.
I need to execute a query to return the list of all title_id's that are related to a given title_id by a common actor. The SQL for this query looks like this:
SELECT DISTINCT r2.title_id
FROM role as r1, role as r2
WHERE r1.name_id = r2.name_id
AND r1.title_id != r2.title_id
AND r1.title_id = <given title_id>
Can something like this be expressed in a single Django ORM query, or am I forced to use two queries with some intervening code? (Or raw SQL?)
Normally I would break this into Actor and Movie table to make it easier to query, but your requirement is there so I will give it a go
def get_related_titles(title_id)
all_actors = Role.objects.filter(title_id=title_id).values_list('pk', flat=True)
return Role.objects.filter(pk__in=all_actors).exclude(title_id=title_id) # maybe u need .distinct() here
this should give you one query, validate it this way:
print(get_related_titles(some_title_id).query)

Django optimized query

I have a model Personne and each person may be linked to another person via a PersonneRelation model:
class Personne(BaseModel):
user = models.OneToOneField(User)
relations = models.ManyToManyField('self', through='PersonneRelation',
symmetrical=False,
related_name='personne_relations')
class PersonneRelation(BaseModel):
type_relation = models.IntegerField(
choices=[(a, b) for a, b in list(PersonneEnums.TAB_RELATIONS.items())],
default=PersonneEnums.RELATION_FRIEND)
src = models.ForeignKey('Personne', related_name='relation_src')
dst = models.ForeignKey('Personne', related_name='relation_dst')
is_reverse = models.BooleanField(default=False)
So, imagine one person A. He has a contact B. My client wants me to display all the contacts of B so that A might be able to send a message to those contacts. Pretty easy. The problem is that I've made a view that displays the "person" information and it's pretty simple, like /person/{id}. Thus if you change the {id} value, you can see another person information. What I need to check is:
if the person to display is a contact of A
if the person to display is a contact... of a contact of A
For now I'm doing an ugly query where I check all the contacts... of all the contacts of A.
How would you do an optimized query to check if the person to display is a contact... of a contact of A?
Returns True, if user can see other's information. user and other are instances of auth.User.
user.personne.relations.filter(
Q(user=other) | Q(relations__user=other)
).exists()

Django query across foreign keys

I need to get a list of users who have been referenced as a ForeignKey in two other models. I'm still a little unclear on queries when they reach this complexity.
Models:
class Page(models.Model):
user = models.ForeignKey(User)
class EmailSent(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=200)
In English, what I want to get is: 10 active users who have 0 pages and have never had an email with the name check_in sent to them.
Here's where I am:
users = User.objects.filter(is_active=1).annotate(page_count=Count('pages')).filter(page_count=0)[10]
but not sure how to do what is essentially:
email_sent = EmailSent.objects.filter(user=user, name='check_in')
Any ideas?
One possible way to get what you want is:
users = User.objects.filter(is_active=1).annotate(page_count=Count('pages')).filter(page_count=0)
EmailSent.objects.filter(user__in=users, name='check_in')[10]
Another way is,
users = User.objects.filter(is_active=1).annotate(page_count=Count('pages')).filter(page_count=0)
users.emailsent_set.all()[10]
Try the following:
users = User.objects.filter(is_active=1)\
.exclude(emailsent__name='check_in')\
.extra(select={'page_count': "select count(*) from YOURAPP_page where user_id = auth_user.id"}, where=['page_count 0'])[:10]