I have this in models:
class CustomUser(AbstractUser):
selectat = models.BooleanField(default=False)
def __str__(self):
return self.username
class Score(models.Model):
VALUE = (
(1, "Nota 1"),
(2, "Nota 2"),
(3, "Nota 3"),
(4, "Nota 4"),
(5, "Nota 5"),
(6, "Nota 6"),
(7, "Nota 7"),
(8, "Nota 8"),
(9, "Nota 9"),
(10, "Nota 10"),
)
user_from = models.ForeignKey(settings.AUTH_USER_MODEL, default=0)
user_to = models.ForeignKey(settings.AUTH_USER_MODEL, default=0, related_name='user_to')
nota = models.PositiveSmallIntegerField(default=0, choices=VALUE)
def __str__(self):
return str(self.user_to)
How can i access the score objects by having the user?
When i give the user to score object i can get the notes.
x = Score.objects.filter(user_to__username='Fane')
x
<QuerySet [<Punctaj: Fane>, <Punctaj: Fane>]>
for a in x:
print(a.nota)
1
5
I want to use something like this:
y = CustomUser.objects.get(id=7)
x = x.score.all()
for a in x:
print(a.nota)
1
5
But this won't work, it's giving me:
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: 'CustomUser' object has no attribute 'score'
You have two foreign keys from CustomUser to Score. The first one, user_from, does not set a related_name, so it uses the default, which is score_set:
x = y.score_set.all()
The second does set a related_name, so you use that:
x = y.user_to.all()
Note that this does not make much sense as a related name, since it points to scores, not users; it should probably be something like scores_to_user.
Related
Hi stackoverflow community, my question is about django annotate.
Basically what I am trying to do is to find duplicated value with same values from two different fields in two different tables.
This is my models.py
class Order(models.Model):
id_order = models.AutoField(primary_key=True)
class OrderDelivery(models.Model):
order = models.ForeignKey(Order, on_delete=models.SET_NULL, null=True, blank=True)
delivery_address = models.TextField()
class OrderPickup(models.Model):
order = models.ForeignKey(Order, on_delete=models.SET_NULL, null=True, blank=True)
pickup_date = models.DateField(blank=True, null=True)
This is my current code:
dup_job = Order.objects.filter(
orderpickup__pickup_date__range=(start_date, end_date)
).values(
'orderdelivery__delivery_address',
'orderpickup__pickup_date',
).annotate(
duplicated=Count('orderdelivery__delivery_address')
).filter(
duplicated__gt=1
)
Based on what I have, I am getting result like this (delivery_address is omitted for privacy purpose):
{'orderdelivery__delivery_address': '118A', 'orderpickup__pickup_date': datetime.date(2022, 3, 9), 'duplicated': 2}
{'orderdelivery__delivery_address': '11', 'orderpickup__pickup_date': datetime.date(2022, 3, 2), 'duplicated': 6}
{'orderdelivery__delivery_address': '11 A ', 'orderpickup__pickup_date': datetime.date(2022, 3, 3), 'duplicated': 5}
{'orderdelivery__delivery_address': '21', 'orderpickup__pickup_date': datetime.date(2022, 3, 10), 'duplicated': 3}
{'orderdelivery__delivery_address': '642', 'orderpickup__pickup_date': datetime.date(2022, 3, 7), 'duplicated': 2}
{'orderdelivery__delivery_address': '642', 'orderpickup__pickup_date': datetime.date(2022, 3, 8), 'duplicated': 2}
{'orderdelivery__delivery_address': 'N/A,5', 'orderpickup__pickup_date': datetime.date(2022, 3, 8), 'duplicated': 19}
Is there a way to get the id_order of those 'duplicated'?
I have tried include id_order in .values() but the output will not be accurate as the annotation is grouping by the id_order instead of delivery_address.
Thank you in advance
You can get the smallest (or largest) item with a Min [Django-doc] (or Max) aggregate:
from django.db.models import Min
dup_job = Order.objects.filter(
orderpickup__pickup_date__range=(start_date, end_date)
).values(
'orderdelivery__delivery_address',
'orderpickup__pickup_date',
).annotate(
min_id_order=Min('id_order')
duplicated=Count('orderdelivery__delivery_address')
).filter(
duplicated__gt=1
)
or for postgresql, you can make use of the ArrayAgg [Django-doc] to generate a list:
# PostgreSQL only
from django.contrib.postgres.aggregates import ArrayAgg
dup_job = Order.objects.filter(
orderpickup__pickup_date__range=(start_date, end_date)
).values(
'orderdelivery__delivery_address',
'orderpickup__pickup_date',
).annotate(
min_id_order=ArrayAgg('id_order')
duplicated=Count('orderdelivery__delivery_address')
).filter(
duplicated__gt=1
)
How to set ordering and number of items for each item in filter list?
class Category(models.Model):
category = models.CharField(max_length = 20)
Class Question(TimeStampedModel):
category = models.ForeignKey(Category, related_name='question_set', on_delete=models.CASCADE)
question = models.TextField(_('Question Text'))
Category.objects.filter(category__in=['Sport', 'History']).values_list('question_set',flat=True).order_by('?')[:3]
<QuerySet [6, 7, 3]>
I need result like:
<QuerySet [1, 3, 2, 7, 11, 9]>
On the template, when I call person.health_issue, I am getting '1','2' instead of 'Abdominal pain','Anaphylaxis'. How to display the value ('Abdominal pain','Anaphylaxis') instead of the code(1 or2 etc).
I tried with this also {{ person.get_health_issue_display }} in template,it is not displayed anything.
forms.py
HEALTH_USSUES = (
('1', 'Abdominal pain'), ('2', 'Anaphylaxis'), ('3', 'Asthma'),
('4', 'Bruising'), ('5', 'Chest pains'), ('6', 'Coughs or Colds')
)
class PersonActionsForm(forms.ModelForm):
action = forms.MultipleChoiceField(widget=forms.Select(), choices=HEALTH_USSUES, required=False)
models.py
class ReportPerson(models.Model):
report = models.ForeignKey(Report)
name = models.CharField('Name', max_length=100)
first_aid = models.BooleanField('First aid', default=False)
health_issue = models.IntegerField(default=0)
views.py
def report_template(request):
""""""
person = ReportPerson.objects.get(pk=person_id)
""""""
return render(request, 'event/print.html',
{
'person':person
})
can any one tell me how to do this.
Thanks
As you don't have any choices set in model field health_issue you need to write the get_health_issue_display method by your self i will name it as health_issue_display so that default get_FOO_display method not gets overridden:
HEALTH_USSUES = (
(1, 'Abdominal pain'), (2, 'Anaphylaxis'), (3, 'Asthma'),
(4, 'Bruising'), (5, 'Chest pains'), (6, 'Coughs or Colds')
)
class ReportPerson(models.Model):
report = models.ForeignKey(Report)
name = models.CharField('Name', max_length=100)
first_aid = models.BooleanField('First aid', default=False)
health_issue = models.IntegerField(default=1)
def health_issue_display(self):
for c in HEALTH_USSUES:
if c[0] == self.health_issue:
return c[1]
Or just add choices in the model field:
health_issue = models.IntegerField(default=1, choices=HEALTH_USSUES)
Now you have get_health_issue_display.
Also make the first value in every choice as integer (1, 'Abdominal pain') rather than string '1'. Just to remove the confusion.
You have default=0 which does not exists in choices. Change it to default=1
Given a product (product_name is a parameter in the view), I am trying to return the 5 top-ranked products within that category (as defined by the method "get_avg_rating") as a list that I can loop through in a template. Any advice on how to do this?
class Productbackup(models.Model):
website = models.CharField('Product name', max_length = 200)
url_friendly = models.CharField('URL friendly', max_length = 200)
website_url = models.URLField('Product URL')
description= models.CharField('Description', max_length = 2000)
category = models.ForeignKey(Categories)
#category = models.ManyToManyField(Categories)
image_hero = models.URLField('Hero image url')
image_second = models.URLField('Second image url')
image_third = models.URLField('Third image url')
created_on = models.DateTimeField(auto_now_add = True)
updated_at = models.DateTimeField(auto_now = True)
def __unicode__(self):
return self.website
def get_avg_rating(self):
reviews = Reviewbackup.objects.filter(product=self)
count = len(reviews)
sum = 0.0
for rvw in reviews:
sum += rvw.rating
return (sum/count)
def get_num_reviews(self):
reviews = Reviewbackup.objects.filter(product=self)
count = len(reviews)
return count
RATING_OPTIONS = (
(1, '1'),
(2, '2'),
(3, '3'),
(4, '4'),
(5, '5'),
(6, '6'),
(7, '7'),
(8, '8'),
(9, '9'),
(10, '10'),
)
class Reviewbackup(models.Model):
review = models.CharField('Review', max_length = 2000)
created_on = models.DateTimeField(auto_now_add = True)
updated_at = models.DateTimeField(auto_now = True)
user = models.CharField('Username', max_length = 200)
rating = models.IntegerField(max_length=2, choices=RATING_OPTIONS)
product = models.ForeignKey(Productbackup)
def __unicode__(self):
return self.review
class Categories(models.Model):
category = models.CharField('Category_second', max_length = 200)
url_friendly = models.CharField('url_friendly', max_length = 200)
def __unicode__(self):
return unicode(self.category)
def view_reviews(request, product_name):
product = get_object_or_404(Productbackup, url_friendly=product_name)
product_id = product.id
#get reviews for the this product
reviews = Reviewbackup.objects.filter(product_id=product_id).order_by("-created_on")
#similar products in category comparison
prod_category = Productbackup.objects.filter(category=product.category)
#top_ranked = Productbackup.objects.order_by('get_avg_rating')[0:5]
#recently added
recent_added = Productbackup.objects.order_by('-created_on')[0:5]
return render_to_response('reserve/templates/view_reviews.html', {'prod_category': prod_category, 'product':product, 'reviews':reviews, 'recent_added':recent_added},
context_instance=RequestContext(request))
You can use annotate for that
from django.db.models import Sum, Avg
def get_top_products(amount=5):
try:
Productbackup.objects.annotate(review_amount=Sum("reviewbackup__product")).\
order_by("review_amount")[:amount]
except Productbackup.DoesNotExist:
return None
This is just a basic example, which can be extended to your needs
Try to implement that:
Create a dictionary. It will have the Product as the key and the rating as the value.
Loop through your items (Product.objects)
In that loop, put each item in the dictionary
Out of that loop, sort the dictionary by value.
(See Sort a Python dictionary by value)
Get the last items of the dictionary (dictionary[-5]).
Note that you will have to handle items with ex-aequo ratings. Indeed, if 10 items have the same score, then your Top 5 doesn't mean anything using the above method.
This would result in a code similar to this:
items_with_rating = {}
for product in Product.objects:
items_with_rating[product] = product.get_avg_rating()
items_sorted_by_rating = sorted(items_with_rating.iteritems(), key=operator.itemgetter(1))
top5_items = items_sorted_by_rating[-5]
Maybe something like this could work?
products = [[prod, prod.get_avg_rating()] for prod in Productbackup.objects()]
top_5_items = sorted(products, key=lambda x: x[1], reverse=True)[:5]
I'm making a website for a client who wants to be able to change then opening hours
for each of his different stores. Is there an existing solution for this type of problem with Django ?
What do you mean? Seems pretty simple. Adjust according to your weekday order. And if you like, add validation. But people should be smart enough to not need validation for that sort of stuff.
HOUR_OF_DAY_24 = [(i,i) for i in range(1,25)]
WEEKDAYS = [
(1, _("Monday")),
(2, _("Tuesday")),
(3, _("Wednesday")),
(4, _("Thursday")),
(5, _("Friday")),
(6, _("Saturday")),
(7, _("Sunday")),
]
class OpeningHours(models.Model):
store = models.ForeignKey("StoreModel")
weekday_from = models.PositiveSmallIntegerField(choices=WEEKDAYS, unique=True)
weekday_to = models.PositiveSmallIntegerField(choices=WEEKDAYS)
from_hour = models.PositiveSmallIntegerField(choices=HOUR_OF_DAY_24)
to_hour = models.PositiveSmallIntegerField(choices=HOUR_OF_DAY_24)
def get_weekday_from_display(self):
return WEEKDAYS[self.weekday_from]
def get_weekday_to_display(self):
return WEEKDAYS[self.weekday_to]
class SpecialDays(models.Model):
holiday_date = models.DateField()
closed = models.BooleanField(default=True)
from_hour = models.PositiveSmallIntegerField(choices=HOUR_OF_DAY_24, null=True, blank=True)
to_hour = models.PositiveSmallIntegerField(choices=HOUR_OF_DAY_24, null=True, blank=True)