Django ratings system - django

I'm working on a project that needs a ratings system where they can rate the quality of the houses. They'd be doing it from the admin panel, so it's not something that a visitor would rate.
For example, for each listing, they can rate it from 1-5 stars:
Location:
Room:
Dining options:
Community:
Overall:
In my models.py file, I have this setup:
Class Listing (models.Model):
...
RATING_CHOICES = (
(1, 1),
(2, 2),
(3, 3),
(4, 4),
(5, 5),
)
location_rating = models.DecimalField(choices = RATING_CHOICES, max_digits=3, decimal_places=2)
room_rating = models.DecimalField(choices = RATING_CHOICES, max_digits=3, decimal_places=2)
dining_options_rating = models.DecimalField(choices = RATING_CHOICES, max_digits=3, decimal_places=2)
community_rating = models.DecimalField(choices = RATING_CHOICES, max_digits=3, decimal_places=2)
recreation_rooms_rating = models.DecimalField(choices = RATING_CHOICES, max_digits=3, decimal_places=2)
def __unicode__(self):
return self.name
def get_avg_rating(self):
avg_rating = (self.location_rating + self.room_rating + self.recreation_rooms_rating + self.dining_options_rating + self.community_rating) / 5
return avg_rating
I'd plan to display the ratings with a little CSS. Just putting room_rating or avg_rating in a template tag doesn't work, so I'm assuming I'd have to add some lines of code to views.py. I've checked the Django docs, but I'm still not sure how to go about this.
This seems like something that should be easy to do, but I'm not really sure where to start.

A potential problem with the direction you taking is that you're only storing one set of ratings for each Listing. If user B wants to rate a Listing, he'll have to overwrite User A's ratings. Instead of putting the ratings inside the Listing, you'll need to break out your ratings into a separate Ratings table, and use a ManytoMany relationship between your Listings and Ratings. That way, you can have multiple users rate a particular listing, and then you can calculate and show the averages for each listing. See this discussion for reference.
It might be better to use an existing ratings app than starting from scratch. They already have documentation so you can get them up and running quickly, and they handle the database design for you.
Django Ratings
Django Valuate

Related

Cyclical operations in Django Rest Framework

In my frontend application I have a panel which shows 6 the most popular products on my site. Searching for the most popular products each request by the number of views can be costly. I think that a good way to improve it will be to create a table in my data base which will store 6 the most popular products and the table will be refreshed for example every one minute. What should I looking for to do so cyclical operation on my django backend?
You can create separate model
models.py
class ProductRecord(models.Model):
product = models.OneToOneField(
'Product'
related_name='stats', on_delete=models.CASCADE)
# Data used for generating a score
num_views = models.PositiveIntegerField(default=0)
num_basket_additions = models.PositiveIntegerField(
default=0)
num_purchases = models.PositiveIntegerField(
default=0)
# Product score - used within search
score = models.FloatField(default=0.00)
def __str__(self):
return _("Record for '%s'") % self.product
rule_engine.py
class Calculator:
weights = {
'num_views': 1,
'num_basket_additions': 3,
'num_purchases': 5
}
def calculate_scores(self):
total_weight = float(sum(self.weights.values()))
weighted_fields = [
self.weights[name] * F(name) for name in self.weights.keys()]
ProductRecord.objects.update(
score=sum(weighted_fields) / total_weight)
receivers.py
Here you'll have multiple signals to receive addition to basket, views, favoriting, and so on..
This is an example
def receive_product_view(sender, product, user, **kwargs):
UserProductView.objects.create(product=product, user=user)
## yyou can do much advanced staff here using signals
Another solution
You can use Redis to save such these data like counts and purchases
Or make aws lambda function for receiving analytical data, all of these solutions depend on your case
This is a pseudo solution. hope it's been beneficial to you

Finding average score from all user's maximum score in django orm

I'm trying to calculate the average score from all finished exam. Users can send an exam as much as they want. So, the data of the finished exam can be like...
(User, Finished exam's score)
(1, 8), (1, 9), (1, 10), (2, 2), (2, 3), (3, 8)
I want average score of all highest score of each user.
A expected average score of above data should be 7. "(10+3+8) / 3"
My django model
class Exam(models.Model):
title = models.TextField("Title")
description = models.TextField("Description")
class Question(models.Model):
exam_id = models.ForeignKey("Exam", on_delete=models.CASCADE)
title = models.CharField(max_length=500, verbose_name="Question")
score = models.FloatField(default=1)
class Answer(models.Model):
question_id = models.ForeignKey("Question", on_delete=models.CASCADE)
title = models.CharField(max_length=500, verbose_name="Answer")
class FinishedExam(models.Model):
exam = models.ForeignKey("Exam", on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
total_score = models.FloatField(default=0)
We can first annotate for every use the maximum score, and then aggregate over that set:
from django.db.models import Avg, Max
User.objects.annotate(
max_score=Max('finishedexam__score')
).aggregate(
avg_score=Avg('max_score')
)['avg_score']
This will yield a query that looks like:
SELECT AVG(max_score)
FROM (
SELECT auth_user.id AS Col1, MAX(core_finishedexam.score) AS max_score
FROM auth_user
LEFT OUTER JOIN core_finishedexam ON auth_user.id = core_finishedexam.user_id
GROUP BY auth_user.id ORDER BY NULL
) subquery
The subquery will thus generate the maximum score per user, and then we calculate the average.
If you want to filter for a specific exam, you can filter with:
from django.db.models import Avg, Max
User.objects.filter(
finishedexam__exam=my_exam
).annotate(
max_score=Max('finishedexam__score')
).aggregate(
avg_score=Avg('max_score')
)['avg_score']
This will then only take the average for the users that made that exam (at least once). So users that never made my_exam will then be ignored.

How to query database with conditional expression in Django?

I have three models: Business, Offers and OfferPlan:
Business:
class Business(models.Model):
name_of_business = models.CharField(max_length=255)
Offers:
class Offers(models.Model):
business = models.ForeignKey(Business, related_name="business_offer",
on_delete=models.CASCADE)
title = models.CharField(max_length=255)
subtext = models.CharField(max_length=255)
OfferPlan:
class OfferPlan(models.Model):
WEEKDAYS = [
(1, _("Monday")),
(2, _("Tuesday")),
(3, _("Wednesday")),
(4, _("Thursday")),
(5, _("Friday")),
(6, _("Saturday")),
(7, _("Sunday")),
]
offer = models.ForeignKey(Offers, related_name="business_offer_plan",
on_delete=models.CASCADE)
weekday = models.IntegerField(
choices=WEEKDAYS,
)
from_hour = models.TimeField()
to_hour = models.TimeField()
I have a ListView which search for businesses open based on different params such as city, category etc. I also want to now search by weekday, say which business is open on Monday will be displayed and which are not wont be displayed on that day. Weekday information is stored in OfferPlan and there could be multiple timings for the offers that day in OfferPlan table, but I want to query (filter, exclude) the businesses who has even a single entry on that weekday number.
Here is my ListView:
class SearchListView(ListView):
template_name = 'search/search.html'
model = Business
def get_queryset(self):
# queryset = Business.objects.filter(business_address__city=AppLocations.objects.first().city)
if 'city' in self.request.GET:
queryset = Business.objects.filter(business_address__city=self.request.GET.get('city'))
if 'category' in self.request.GET:
queryset = queryset.filter(category__code=self.request.GET.get('category'))
# if 'date' not in self.request.GET:
# queryset = B
raise
return queryset
How could this be possible? Also looked into https://docs.djangoproject.com/en/1.8/ref/models/conditional-expressions/ but not able to figure out.
Thanks
Update 1
After researching more in the web, I figured out this is how it could be achieved, but need to know for sure from other Django enthusiasts here that it is right.
queryset.filter(business_offer__business_offer_plan__weekday=1).annotate(count_entry=Count('business_offer__business_offer_plan__weekday')).filter(count_entry__gt=1)
Solution
Jefferson's solution was tagged as right answer as it provided more insights, about which query is fast and what wrong was with my previous update, so here is the proper solution to which we both agreed:
queryset.filter(business_offer__business_offer_plan__weekday=1).annotate(count_entry=Count('business_offer__business_offer_plan__weekday')).filter(count_entry__gte=1)
def get_query(weekday):
businesses = Business.objects.filter(business_offer__in=Offers.objects.filter(
business_offer_plan__in=OfferPlan.objects.filter(weekday=weekday))).distinct()
return businesses
There's a heavy query, but it works.
There's no conditional expression here - and your annotation is much too complicated. You just need an additional filter.
queryset.filter(business_offer__business_offer_plan__weekday=self.request.GET['weekday'])

django sum of inline class price

Here is the related App and Classes. Trying to get the sum of amount of all the components belonging to an invoice and save it in 'sub_total' field of Invoice. An invoice sub_total = sum of amount of all components of the invoice instance. What will be way around? Thanks in advance.
from stock.models import Part
class Invoice(models.Model):
invoice_number = models.CharField(max_length=250)
sub_total = models.DecimalField(max_digits=8, decimal_places=2)
class Component(models.Model):
invoice = models.ForeignKey('Invoice',)
stock = models.ForeignKey('stock.Part',)
qty = models.SmallPositiveIntegerField()
unit_price = models.DecimalField(max_digits=8, decimal_places=2)
amount = models.DecimalField(max_digits=8, decimal_places=2)
After hours of trying things out, I finally got a clue and things are working fine now. I did not want complicated things in views or in the templates.
Below link has a fine example, and learnt from below and further testing seems to be working fine.
https://github.com/mwolff44/django-simple-invoice/blob/master/invoice/models.py
Around line number 194, something like below is shown, which tells us the clue as to what to do.
Below is similar to my "Invoice" Class
....
def total(self):
total = Decimal('0.00')
for item in self.items.all():
total = total + item.total()
return total
Then in Item (Component, in my case) Class. Around line number 281, something like below is shown. Although this has been taken care of earlier, thought others might find this useful.
...
def total(self):
total = Decimal(str(self.unit_price * self.quantity))
return total.quantize(Decimal('0.01'))
Again, big thanks to Mathias WOLFF, https://github.com/mwolff44

Advice on how to structure a Location model with Django and PostgreSQL (postGIS)

stack has helped me out when I needed it the most and I'm going to test my luck again. Here's the scenario. Currently I have the following model:
class Location(models.Model):
name = models.CharField(max_length=200, blank=True)
address = models.CharField(max_length=255, blank=True)
city = models.CharField(max_length=140, null=True, blank=True)
province_state = models.CharField(max_length = 140, null=True, blank=True)
country = models.CharField(max_length=2, null=True, blank=True)
postal_code = models.CharField(max_length=32, null=True, blank=True)
foursquare_id = models.CharField(max_length=100, blank=True, unique=True)
lat = models.DecimalField(max_digits=18, decimal_places=15, null=True, blank=True)
lon = models.DecimalField(max_digits=18, decimal_places=15, null=True, blank=True)
Looks fairly straight forward right? Now what I do is I return back all locations that are within 100 KM of a user's current location. Each degree is approximately 100 KM. So for example, between a latitude of 10 degrees and 11 degrees, it is about distance of 100 KM. Same with longitude. The 1 degree is approx ~ 100 km. Here is my WhatsNearBy view:
#csrf_exempt
def WhatsNearBy(request):
if request.META.has_key('HTTP_AUTHORIZATION') and request.method == 'GET':
authmeth, auth = request.META['HTTP_AUTHORIZATION'].split(' ', 1)
if authmeth.lower() == 'basic':
auth = auth.strip().decode('base64')
username, password = auth.split(':', 1)
authuser = authenticate(username=username, password=password)
if authuser:
if authuser.is_active:
client_lat = int(request.GET.get('lat'))
client_lon = int(request.GET.get('lon'))
queryset = Location.objects.filter(__lat__gte = (client_lat - 1),
lat__lte = (client_lat + 1),
lon__gte = (client_lon - 1),
lon__lte = (client_lon + 1))
return queryset
The above queryset returns back all the Locations that are approximately 100 KM away. This works great and it has been quite solid. However, my concern is that the filtering will sooner or later become slow as the database gets larger.
I heard about postGIS but I'm not sure if it would be more efficient than what I am doing now. Would I have to migrate the Location model to postGIS? how would this all work? and once I do would I be able to find all the locations within a certain distance?
Looking forward to seeing your solutions!
I dealt with a similar problem recently. I found that PostGIS and other spatial solutions were overkill for finding all of the points in an approximate radius.
I ended up using the SAS distance algorithm in the database. After filtering with a square, I used the raw command to let the database crunch the numbers for me. Postgres and most other databases other than SQLite support trig functions, so you can filter on the database's computed distance. This is the formula in miles, but you can change the constant.
Dist = 3949.99 * arcos(sin(Y1) * sin(Y2) + cos(Y1) * cos(Y2) * cos(X1 - X2));
http://support.sas.com/kb/3/973.html
Edit: To expand, I was querying all zip codes (~44K) in the US within a radius. It turns out I could query all of the zip codes within 500 miles in fractions of a second that were acceptably fast for my purposes. The geospatial libraries allow you to find all of the points within a complex polygon much more quickly, but I don't expect you'll see a noticeable performance improvement for your application.