I am implementing a friend system similar to facebook where one can send friendship requests and accept requests and then see their friends. On the page where one sees the friend requests I am trying to filter out the users who have accepted the friend request and are friend already now. In the code below, 'u' is the current logged in user. Friendship table hold two fields , both foreign keys, please see below:
try:
already_friends = Friendship.objects.get(Q(from_friend=u) | Q(to_friend=u))
for x in already_friends.iterator():
my_requests = FriendRequest.objects.filter(Q(receiver=request.user) & ~Q(sender=x))
except ObjectDoesNotExist:
class FriendRequest(models.Model):
sender = models.ForeignKey(User, related_name='the_sender',on_delete=models.CASCADE)
receiver = models.ForeignKey(User, related_name='the_receiver', on_delete=models.CASCADE)
def __str__(self):
return "request sent"
class Meta:
unique_together = (('sender', 'receiver'),)
class Friendship(models.Model):
from_friend = models.ForeignKey(User, related_name="from_friend", on_delete=models.CASCADE)
to_friend= models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return "Friend request accepted"
class Meta:
unique_together = (('from_friend', 'to_friend'),)
When I use the query I wrote at the top using Q (complex queries) and then use iterator, I get the following error:
'Friendship' object has no attribute 'iterator'
How can I achieve what I intend to do using django models/queries?
You should use .filter(…) [Django-doc] not .get(…) [Django-doc]: .get(…) retrieves a single Friendship object, and will raise an error if there is no such object, or if there are multiple ones. .filter(…) on the other hand will return a (possibly empty) queryset of all Friendship records that satisfy the given predicate:
already_friends = Friendship.objects.filter(Q(from_friend=u) | Q(to_friend=u))
for x in already_friends.iterator():
my_requests = FriendRequest.objects.filter(Q(receiver=request.user) & ~Q(sender=x))
Furthermore it makes no sense to use Q(sender=x): x is a Friendship object, not a user object. You probably want to check if the from_friend_id or to_friend_id is the u, and thus use the other one to filter. Something like:
already_friends = Friendship.objects.filter(Q(from_friend=u) | Q(to_friend=u))
for x in already_friends.iterator():
if x.from_user_id = u.id:
my_requests = FriendRequest.objects.filter(receiver=request.user, sender_id=x.to_user_id)
else:
my_requests = FriendRequest.objects.filter(receiver=request.user, sender_id=x.from_user_id)
Related
so I'm trying to build a Ledger App. When new users sign-ups, they create a new Business account that is linked to them (ForeignKey). Here is my model:
User = get_user_model()
class Business_Account(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
business_name = models.CharField(max_length=50)
Type_of_service = models.CharField(choices=SERVICE_CATEGORIES, max_length=50)
business_email_address = models.EmailField(max_length=254)
class Meta:
verbose_name = "Business_Account"
verbose_name_plural = "Business_Accounts"
def __str__(self):
return self.business_name
Now, I want to make a get request view that can query and returns a business account that belongs to a particular user. My current view just returns all the business accounts available in the database irrespective of which user is login. Here is my view:
class AddBusinessAcctView(APIView):
def get_object(self):
try:
return Business_Account.objects.all()
except:
raise status.HTTP_404_NOT_FOUND
def get(self,request):
queryset = self.get_object()
serializer = BusinessAcctSerializer(queryset, many=True)
return Response(data=serializer.data, status=status.HTTP_200_OK)
Now, How can I query business accounts that belong to a particular user?. Thanks in advance
Normally, you'd have authentication and a logged-in user that you were trying to retrieve information for. Assuming you're going to do this, that user is available in the request object.
Business_Account.objects.get(user=request.user)
But in general, you can just do:
user = User.objects.get(username='john')
Business_Account.objects.get(user=request.user)
# or
Business_Account.objects.get(user_id=123456)
I finally got it. It is something similar to the above answer.
queryset = Business_Account.objects.filter(user=request.user)
I have the following two models:
class URLResource(models.Model):
class ResourceType(models.TextChoices):
VID = 'VID', _('Video')
DOC = 'DOC', _('Document')
resource_type = models.CharField(
choices=ResourceType.choices,
default=ResourceType.UNK,
max_length=3)
title = models.CharField(max_length=280, null=True)
url = models.URLField()
created = models.DateTimeField(auto_now_add=True)
def __repr__(self):
return f'URLResource {self.resource_type}: {self.title} URL: {self.url}'
class Record(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
...)
date = models.DateTimeField(default=now, blank=True)
# <resource>: I want to point to a resource somehow
def __repr__(self):
return f'Record {{ User: {self.user}, Resource: }}'
My use case is such that a lot of users can create Records and list them, but since the underlying URLResource is going to be same, I thought I could use many-to-one relationship from Records-to-URLResource so the underlying resources would remain the same. However, even though I have only one type of resource now i.e. URLResource, I also want to roll out other resources like VideoResource, PhysicalResource etc. Clearly, I can't simply use the many-to-one relationship. So, I figured I could use Django's contenttype framework, but I still can't get things to work.
I want to be able to do atleast these two things:
When creating a Record, I can easily assign it a URLResource (or any other Resource) (but only one resource).
I can easily access the URLResource fields. For example like Record.objects.get(user=x).resource.resource_type.
I am trying to add a condition to my notification to check if a user's total number of likes for all the posts related to him and send a notification if reached a certain number. I have filtered the total likes by the post author but I keep receiving AttributeError: 'Like' object has no attribute 'filter'
To summarize here is the post model
class Post(models.Model):
author = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='author')
num_likes = models.IntegerField(default=0, verbose_name='No. of Likes')
likes = models.ManyToManyField(User, related_name='liked', blank=True)
Here is the likes model
class Like(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
value = models.CharField(choices=LIKE_CHOICES,
default='Like', max_length=8)
def like_progress(sender, instance, *args, **kwargs):
like = instance.filter(post__author=Post.author).count()
post = like.post
sender = like.user
if like == 12:
notify = Notification(post=post, sender=sender,
user=post.author, notification_type=3)
notify.save()
post_save.connect(Like.like_progress, sender=Like)
My Question: How to fix this error?
The problem is you are calling a Model's function on an instance. I recommend you read about the Django queries.
Basically that error happens because the filter() function can be used from a model's manager or an existing queryset, and you are using it from an instance of the model.
Here's a quick example to make it clearer:
This is a queryset, you can get one by using a model's manager aka the objects attribute.
from myapp.models import MyModel
queryset = MyModel.objects.filter(my_attribute=True)
The resulting queryset can be further filtered
queryset2 = queryset.filter(some_other_attribute=False)
Now to put it simple, a queryset can be seem as a list of instances. In this example from the MyModel's model and you can get any instance from the queryset or iterate the queryset and go through all the instances.
instance_1 = queryset2.first()
instance_2 = queryset2.last()
# or
for instance in queryset2:
print('This is an instance', instance)
Instances do not have the same filter() functionality as a manager or a queryset as they are a single element that cannot be filtered further.
So this this will raise an AttributeError
try:
instance_1.filter(my_attribute=False)
except AttributeError:
pritn("This won't work!")
So getting to your example, the correct way to do it should look something like this:
def like_progress(sender, instance, created=None, *args, **kwargs):
# You may want to stop this if the objects was updated and not created.
if not created:
return
# get all likes from the author
qs_likes = Like.objects.filter(post__author=instance.author)
likes_count = qs_likes.count()
# You can get the post and the sender from the current instance
post = instance.post
sender = instance.user
if likes_count == 12:
notify = Notification.objects.create(post=post, sender=sender,
user=post.author, notification_type=3)
On a side note, you should not be connecting Model's functions the signals as they do receive different arguments in different order. So just define like_progress outside the model.
like = instance.filter(post__author=Post.author).count()
You should use filter this way:
like = Like.objects.filter(post__author=post.author).count()
#note that it should be post.author not Post.author
This will eliminate your current errors, but I doubt you can achieve what you want. I guess what you are trying to do is whenever there is a like instance saved, you want to count the number of conts that the post.author received, and if the like-count is 12, you send a notification.
But the like_progress is a method, are you going to call it by like.like_progress?
Google has lots of examples of doing reverse lookup in the interactive prompt but none of doing them inside a django model as a method.
I have the following models.py file:
class Client(models.Model):
...
def __unicode__(self):
return ???
class ClientDetails(models.Model):
client = models.ForeignKey(Client, null=True)
created = models.DateTimeField(default=datetime.now)
created_by = models.ForeignKey(User, null=True)
name_title = models.CharField(max_length=3, choices=NAME_TITLE_CHOICES)
first_name = models.CharField(max_length=40)
middle_name = models.CharField(max_length=40)
last_name = models.CharField(max_length=40)
...
How do I get the client method to return last_name from ClientDetails?
If a ClientDetails object should only ever be associated with a single Client object, then I would change your FK to a OneToOneField, which will provide you with a neat reverse accessor that can only ever link between a given Client and its associated ClientDetails. Then you can do:
try:
return self.clientdetails.last_name
except ClientDetails.DoesNotExist:
##handle it here, returning a graceful message
Alternatively, if you kept it as a FK, then you'd have to do something like:
try:
self.clientdetails_set.all()[0].last_name
except IndexError, e:
## handle exception here
but using a FK here is brittle and not great form (as the exception-handling implies: if there is none returned, then you'll get an IndexError. Also, there might be more than one ClientDetails object linked to that Client, and you'd only get the details of the first one here.)
So, I really would recommend using a OneToOneField instead of that FK. (All a OneToOneField is basically an FK with unique=True set on it and some neater accessors than standard FKs)
For that you can use in client the *clientdetails_set* to access all the ClientDetails objects that are linked to that client.
The set is an object set from Django, so calling the method all() will retrieve every of the objects. If you know there's only one you can do self.clientdetails_set.all()[0].last_name
Here's the link for the django documentation: Link
ordinarily if I were writing a sql statement to this I would do something like this,
SELECT * FROM (django_baseaccount LEFT JOIN django_account ON django_baseaccount.user_id = django_account.baseaccount_ptr_id)
LEFT JOIN django_address ON django_account.baseaccount_ptr_id = django_address.user_id;name
how do I put this into the Djagno way of querying the database using the API, i.e.
TradeDownloads.objects.filter(online=1)[:6]
My models
BASE ACCOUNT
class BaseAccount(models.Model):
user = models.ForeignKey(User, unique=True)
def __unicode__(self):
"""
Return the unicode representation of this customer, which is the user's
full name, if set, otherwise, the user's username
"""
fn = self.user.get_full_name()
if fn:
return fn
return self.user.username
def user_name(self):
"""
Returns the full name of the related user object
"""
return self.user.get_full_name()
def email(self):
"""
Return the email address of the related user object
"""
return self.user.email
ACCOUNT
class Account(BaseAccount):
"""
The account is an extension of the Django user and serves as the profile
object in user.get_profile() for shop purchases and sessions
"""
telephone = models.CharField(max_length=32)
default_address = models.ForeignKey(Address, related_name='billing_account', blank=True, null=True)
security_question = models.ForeignKey(SecurityQuestion)
security_answer = models.CharField(max_length=200)
how_heard = models.CharField("How did you hear about us?", max_length=100)
feedback = models.TextField(blank=True)
opt_in = models.BooleanField("Subscribe to mailing list", help_text="Please tick here if you would like to receive updates from %s" % Site.objects.get_current().name)
temporary = models.BooleanField()
def has_placed_orders(self):
"""
Returns True if the user has placed at least one order, False otherwise
"""
return self.order_set.count() > 0
def get_last_order(self):
"""
Returns the latest order that this customer has placed. If no orders
have been placed, then None is returned
"""
try:
return self.order_set.all().order_by('-date')[0]
except IndexError:
return None
def get_currency(self):
"""
Get the currency for this customer. If global currencies are enabled
(settings.ENABLE_GLOBAL_CURRENCIES) then this function will return
the currency related to their default address, otherwise, it returns
the site default
"""
if settings.ENABLE_GLOBAL_CURRENCIES:
return self.default_address.country.currency
return Currency.get_default_currency()
currency = property(get_currency)
def get_gateway_currency(self):
"""
Get the currency that an order will be put through protx with. If protx
currencies are enabled (settings.ENABLE_PROTX_CURRENCIES), then the
currency will be the same returned by get_currency, otherwise, the
site default is used
"""
if settings.ENABLE_PROTX_CURRENCIES and settings.ENABLE_GLOBAL_CURRENCIES:
return self.currency
return Currency.get_default_currency()
gateway_currency = property(get_gateway_currency)
ADDRESS
class Address(models.Model):
"""
This class encapsulates the data required for postage and payment mechanisms
across the site. Each address is associated with a single store account
"""
trade_user = models.BooleanField("Are you a stockist of N Products", help_text="Please here if you are a Stockist")
company_name = models.CharField(max_length=32, blank=True)
line1 = models.CharField(max_length=200)
line2 = models.CharField(max_length=200, blank=True)
line3 = models.CharField(max_length=200, blank=True)
city = models.CharField(max_length=32)
county = models.CharField(max_length=32)
postcode = models.CharField(max_length=12)
country = models.ForeignKey(Country)
account = models.ForeignKey('Account')
class Meta:
"""
Django meta options
verbose_name_plural = "Addresses"
"""
verbose_name_plural = "Addresses"
def __unicode__(self):
"""
The unicode representation of this address, the postcode plus the county
"""
return ', '.join((self.postcode, str(self.county)))
def line_list(self):
"""
Return a list of all of this objects address lines that are not blank,
in the natural order that you'd expect to see them. This is useful for
outputting to a template with the aid of python String.join()
"""
return [val for val in (self.line1, self.line2, self.line3, self.city, self.county, self.postcode, self.country.name) if val]
"ordinarily if I were writing a sql statement"
Welcome to ORM. You're not writing SQL so remove this from the question. Do not ever post SQL and ask how to translate SQL into ORM. Translating SQL limits your ability to learn. Stop doing it.
Write down what the result is supposed to be.
It appears that you are getting all Account objects. Period.
At some point in a view function or template you want to get an Address, also.
for a in Account.objects.all():
a.default_address # this is the address that SQL brought in via a "join".
That's it. Please actually do all the examples in the Django tutorial. Actually type the code from the examples and see how it works.
All "join" operations are SQL workarounds. They're a weird SQL-ism, and have nothing to do with the underlying objects. So stop using SQL terminology to describe what you want.
Django provides a clean way to fall-back to native SQL for complex queries see the official documentation: Performing raw SQL queries
Forget the SQL. What do you want to achieve from this query? What do you want to do with the results?
You haven't posted your models. Do they have the foreign keys defined? Can you just do a simple query and use select_related() to get the joined objects?
Edited to add What was wrong with the answer given the previous time you asked this question?
Edited again but everyone has shown you how to get the item via the foreign key! Forget the id, you don't need it. If you have an Account object a, you just do a.default_address to get the actual Address object that is related. If that doesn't work, then you're not posting the right models, as that will definitely work with the models you have posted.