in my Django 1.5 model I'm trying to create a form that let me book a place in a study room.
The study room is defined as follows in models.py:
class StudyRoom(models.Model):
name = models.CharField(max_length = 30, primary_key = True)
city = models.CharField(max_length = 30)
placesno = models.IntegerField()
def __unicode__(self):
return self.name
and this is the relative form:
class SearchSeat(forms.Form):
morning = 'AM'
evening = 'PM'
daysection_choices = ((morning, 'Morning'), (evening, 'Evening'),)
city = forms.ChoiceField(choices = [], required=True, label='Select a city?')
study_room = forms.ChoiceField(choices = [], required=True, label='Select a study room?')
day = forms.DateField(label = 'Select a day', required=True, widget=forms.extras.SelectDateWidget(years=range(2014, 2015)))
section = forms.ChoiceField(choices=daysection_choices, label = 'Morning (form 8.00 to 13.00) or evening (from 13.00 to 18..)?')
def __init__(self, *args, **kwargs):
super(SearchSeat, self).__init__(*args, **kwargs)
self.fields['city'].choices = StudyRoom.objects.all().values_list("city","city").distinct()
search_city = self.fields['city']
self.fields['study_room'].choices = StudyRoom.objects.filter(city = search_city).values_list("name")
The objective was to let the user select the city and then filter the study room and show only the ones in the selected city, all in one form without changing page.
The code written like this doesn't work and I'm starting to think that there isn't a solution without using client side side scripting (it's a Django+Python project so we are supposed to use only those two instruments).
For your problem there can only be a solution with client scripting:
At the moment the html is created, the study-room choices are not determined. This means that on city change, you will need to manipulate your html, which means client side programming like:
$(document).ready(function(){
$('#id_city').on('change', function(){
...
})
);
There is no need for an ajax request, though: you could save the choices into a 'data' attribute in your html and access it using: http://api.jquery.com/data/
You would need then to modify your fields:
self.fields['city'] = forms.Select(attrs={'data-london':'[json_dump of londondata], 'data-paris': '[json_dump of paris study rooms]' etc})
Depending on the amount of data, the ajax call would be a cleaner solution
Related
This is a very simple question that got me stuck. I have 2 tables that are connected: Dealershiplistings, Dealerships. On my website I need to display the Model, Make and year of the car (Which are all stored in the DealershipListing, so i have no problem wiht this part) but I also need to print the address that is stored in the Dealerships table. Can anyone help me with this?
this is what i have for my views.py
def store(request):
dealer_list = Dealer.objects.all()
car_list = DealershipListing.objects.all()
context = {'dealer_list': dealer_list, 'car_list': car_list}
return render(request, 'store/store.html', context)
i tried doing
{{%for car in car_list%}}
<h6>{{car.year}} {{car.make}} {{car.model}}</h6>
{% endfor %}
which works perfectly displaying those. But now how do i display the address of the dealership that is selling the car?
models.py
class Dealer(models.Model):
dealersName = models.TextField(('DealersName'))
zipcode = models.CharField(("zipcodex"), max_length = 15)
zipcode_2 = models.CharField(("zipCode"), max_length = 15)
state = models.CharField(("state"), max_length=5)
address = models.TextField(("Address"))
dealerId = models.IntegerField(("ids"), primary_key=True)
def __str__(self):
return self.dealersName
class DealershipListing(models.Model):
carID = models.IntegerField(("CarID"), primary_key=True)
price = models.IntegerField(('price'))
msrp = models.IntegerField(('msrp'))
mileage = models.IntegerField(('mileage'))
is_new = models.BooleanField(('isNew'))
model = models.CharField(("Models"), max_length= 255)
make = models.CharField(("Make"), max_length=255)
year = models.CharField(("Year"),max_length=4)
dealerID = models.ForeignKey(Dealer, models.CASCADE)
def __str__(self):
return self.year + " " + self.make + " " + self.model
So then it looks like your question is really How do I access data from a foreign key in a template?
The answer is refreshingly simple!
{{car.dealerID.address}}
On a side note, you might want to rename dealerID to dealer, django will handle the db column names how it sees fit, so while you might access the data with .dealer the db column would be named dealer_id by django automatically. Renaming the field also makes it more obvious that accessing it will give you a dealer and not its id.
calling with the model name is what I prefer to use
{{obj.related_table.field_name}}
I think this pattern may help you solve problem related to getting related field value
I need to filter all Experts by past objectives.
I have a minimal runnable example at https://github.com/morenoh149/django-rest-datatables-relations-example (btw there are fixtures you can load with test data).
my models are
class Expert(models.Model):
name = models.CharField(blank=True, max_length=300)
class Meeting(models.Model):
user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)
expert = models.ForeignKey(Expert, on_delete=models.SET_NULL, blank=True, null=True)
objective = models.TextField(null=True, blank=True)
my datatables javascript
$("#table-analyst-search").DataTable({
serverSide: true,
ajax: "/api/experts/?format=datatables",
ordering: false,
pagingType: "full_numbers",
responsive: true,
columns: [
{
data: "objectives",
name: "objectives",
visible: false,
searchable: true,
render: (objectives, type, row, meta) => {
return objectives;
}
},
],
});
My serializer
class ExpertSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(read_only=True)
objectives = serializers.SerializerMethodField()
class Meta:
model = Expert
fields = (
"id",
"objectives",
)
def get_objectives(self, obj):
request = self.context["request"]
request = self.context["request"]
meetings = Meeting.objects.filter(
analyst_id=request.user.id, expert_id=obj.id
).distinct('objective')
if len(meetings) > 0:
objectives = meetings.values_list("objective", flat=True)
objectives = [x for x in objectives if x]
else:
objectives = []
return objectives
When I begin to type in the datatables.js searchbar I get an error like
FieldError at /api/experts/
Cannot resolve keyword 'objectives' into field. Choices are: bio, company, company_id, created_at, description, email, favoriteexpert, first_name, id, is_blocked, last_name, meeting, middle_name, network, network_id, position, updated_at
Request Method: GET
Request URL: http://localhost:8000/api/experts/?format=datatables&draw=3&columns%5B0%5D%5Bdata%5D=tags&columns%5B0%5D%5Bname%5D=favoriteexpert.tags.name&columns%5B0%5D%5Bsearchable%5D=true&columns%5B0%5D%5Borderable%5D=false&columns%5B0%5D%5Bsearch%5D%5Bvalue%5D=&columns%5B0%5D%5Bsearch%5D%5Bregex%5D=false&columns%5B1%5D%5Bdata%5D=desc&columns%5B1%5D%5Bname%5D=&columns%5B1%5D%5Bsearchable%5D=false&columns%5B1%5D%5Borderable%5D=false&columns%5B1%5D%5Bsearch%5D%5Bvalue%5D=&columns%
fwiw, in pure django orm what I want to accomplish would be something like
Expert.objects.filter(
pk__in=Meeting.objects.filter(
objective__icontains='Plastics', user=request.user
).values('expert')
)
How can I filter experts by historical meeting objectives?
The reason for the error is that django-rest-framework-datatables is trying to translate the request into a query which can be run against the Expert table.
In your JS, you're asking for a field called 'objectives' to be returned, but there is no such field on the Expert model.
You could probably achieve what you are trying to do using the django-filter integration. In this case, you could set up a filter on the FK reference to the Meeting table. The example app demonstrates how to do this.
I think the best way to understand what's going on is to get the example application running, and if possible, set breakpoints and step through.
Incidentally, if you want to get the search box to work correctly, then you need to define a global_q() method. This is also covered in the example app.
I ended up authoring a custom django-filter
class AssociatedMeetingCharFilter(filters.CharFilter):
def global_q(self):
"""
Uses the global filter to search a meeting field of meetings owned by the logged in user
"""
if not self._global_search_value:
return Q()
kw = "meeting__{}__{}".format(self.field_name, self.lookup_expr)
return Q(**{
kw: self._global_search_value,
"meeting__user_id": self.parent.request.user.id or -1,
})
class ExpertGlobalFilterSet(DatatablesFilterSet):
name = GlobalCharFilter(lookup_expr='icontains')
objectives = AssociatedMeetingCharFilter(field_name='objective', lookup_expr='icontains')
full example at https://github.com/morenoh149/django-rest-datatables-relations-example
Localhost Screenshot
I have following requirement:
There are exercises and there are ratings
An exercise can have multiple ratings
My goal is to add ratings to the exercises dynamically i.e. I have created all the exercises before hand and rating to a particular exercise I should be able add afterwards calling some function. When I hit 'save_rating' view I get the error "Cannot assign "[]": "Rating.exercise" must be a "Exercise" instance."
What am I doing wrong?
My models.py looks like following
class Exercise(models.Model):
#Field for storing exercise type
EXERCISE_TYPE_CHOICES = (
('BS', 'Best stretch'),
('BR', 'Butterfly reverse'),
('SR', 'Squat row'),
('PL', 'Plank'),
('PU', 'Push up'),
('SP', 'Side plank'),
('SQ', 'Squat'),
)
exercise_type = models.CharField(max_length=5,choices=EXERCISE_TYPE_CHOICES)
#Field for storing intensity level
INTENSITY_LEVEL_CHOICES = (
(1, 'Really simple'),
(2, 'Rather Simple'),
(3, 'Simple'),
(4, 'Okay'),
(5, 'Difficult'),
(6, 'Rather Difficult'),
(7, 'Really Difficult'),
)
intensity_level = models.IntegerField(choices=INTENSITY_LEVEL_CHOICES)
#Field for storing video url for a particular exercise
video_url = models.URLField()
#Field for storing description of the exercise
description = models.CharField(max_length=500)
class Rating(models.Model):
#Field for storing exercise type
exercise = models.ForeignKey(Exercise, related_name='ratings', blank=True, null=True)
#Field for storing rating
RATING_CHOICES = (
('H', 'Happy'),
('N', 'Neutral'),
('S', 'Sad'),
)
value = models.CharField(max_length=1,choices=RATING_CHOICES)
I have defined my serializer like following:
class RatingSerializer(serializers.Serializer):
class Meta:
model = Rating
fields = ('value')
class ExerciseSerializer(serializers.Serializer):
pk = serializers.IntegerField(read_only=True)
exercise_type = serializers.CharField(required=True, allow_blank=False)
intensity_level = serializers.IntegerField(required=True)
video_url = serializers.URLField(required=True)
description = serializers.CharField(required=True, allow_blank=False)
ratings = RatingSerializer(many=True, read_only=True)
My views:
def exercise_list(request):
"""
List all exercises.
"""
if request.method == 'GET':
exercises = Exercise.objects.filter(exercise_type='BS', intensity_level=1)
serializer = ExerciseSerializer(exercises, many=True)
return JSONResponse(serializer.data)
def save_rating(request):
"""
Save a rating for a specific exercise.
"""
#Get a specific exercise for which you want to save the rating
specificExercise = Exercise.objects.filter(exercise_type='BS' , intensity_level=1)
#Create a rating and pass the specific exercise reference to it
Rating.objects.create(exercise = specificExercise, value='H')
#serializer = ExerciseSerializer(rating, many=True)
serializer = ExerciseSerializer(instance=specificExercise)
return JSONResponse(serializer.data)
In your function save_rating, you are having specificExercise as a queryset not one Exercise instance, so when you do:
Rating.objects.create(exercise=specificExercise, value='H')
You try to assign a queryset to your exercise field, which of course doesn't work.
Simple fix would be using get instead of filter so you got one instance. But you also have to make sure the get query doesn't throw DoesNotExist exception, so a simple try except would do.
Edit:
def save_rating(request):
"""
Save a rating for a specific exercise.
"""
# Get a specific exercise for which you want to save the rating
try:
specificExercise = Exercise.objects.get(exercise_type='BS',
intensity_level=1)
# Create a rating and pass the specific exercise reference to it
Rating.objects.create(exercise = specificExercise, value='H')
# serializer = ExerciseSerializer(rating, many=True)
serializer = ExerciseSerializer(instance=specificExercise)
return JSONResponse(serializer.data)
except Exercise.DoesNotExist:
# TODO: you need to return some error indication that there's no such exercise
return None
Please read django doc about making queries.
This Is my models.py
class Customer(models.Model):
def __unicode__(self):
return self.name
name = models.CharField(max_length=200)
type = models.ForeignKey(Customer_Type)
active = models.BooleanField(default=True)
class Sale(models.Model):
def __unicode__(self):
return "Sale %s (%i)" % (self.type, self.id)
customer = models.ForeignKey(Customer)
total = models.DecimalField(max_digits=15, decimal_places=3)
class Unitary_Sale(models.Model):
book = models.ForeignKey(Book)
quantity = models.IntegerField()
unit_price = models.DecimalField(max_digits=15, decimal_places=3)
sale = models.ForeignKey(Sale)
Views.py
def get_filter_result(self, customer_type='' volume_sale=''):
qdict = {}
if customer_type != '':
qdict['type__name'] = customer_type
qdict['active']=True
#WHAT I AM GOING TO DO NEXT
*** if volume_sale != '':
pass # This point I am asking :)
#IT RETURN CUSTOMERS BASE ON PARAMS.
queryset = Customer.objects.filter(**qdict)
***The volume_sale is:
units=Unitary_Sale.objects.all()
>>> units=Unitary_Sale.objects.all()
>>> for unit in units:
... print unit.sale.customer
... print unit.book,unit.sale.total
...
Sok nara
Khmer Empire (H001) 38.4
Sok nara
killing field (H001) 16
San ta
khmer krom (H001) 20
San ta
Khmer Empire (H001) 20
>>>
{<Customer: Sok nara>: Decimal("56.4"), <Customer: san ta>: Decimal("40")}
Decimal("56.4") , Decimal("40") this is the volume_sale
I could not find the ways to make the filter from difference object as in my case.
It will be great if everyone here help in this stuck? Thanks.
Cool, this actually pretty easy to implement. I used the django annotation feature documented here and here:
from django.db.models import Sum
query = Customer.objects.all().annotate(volume_sale = Sum('Sale__total'))
query.filter(volume_sale < 12.0) #return all customers that have purchased less than 12.0
query[0].volume_sale #you can even get it on the Customer objects given back
Django will take care of the database joins for you. It will put this extra field into each instance of the model that you can filter, order_by and access in templates or views.
What I want is to be able to get this weeks/this months/this years etc. hotest products. So I have a model named ProductStatistics that will log each hit and each purchase on a day-to-day basis. This is the models I have got to work with:
class Product(models.Model):
name = models.CharField(_("Name"), max_length=200)
slug = models.SlugField()
description = models.TextField(_("Description"))
picture = models.ImageField(upload_to=product_upload_path, blank=True)
category = models.ForeignKey(ProductCategory)
prices = models.ManyToManyField(Store, through='Pricing')
objects = ProductManager()
class Meta:
ordering = ('name', )
def __unicode__(self):
return self.name
class ProductStatistic(models.Model):
# There is only 1 `date` each day. `date` is
# set by datetime.today().date()
date = models.DateTimeField(default=datetime.now)
hits = models.PositiveIntegerField(default=0)
purchases = models.PositiveIntegerField(default=0)
product = models.ForeignKey(Product)
class Meta:
ordering = ('product', 'date', 'purchases', 'hits', )
def __unicode__(self):
return u'%s: %s - %s hits, %s purchases' % (self.product.name, str(self.date).split(' ')[0], self.hits, self.purchases)
How would you go about sorting the Products after say (hits+(purchases*2)) the latest week?
This structure isn't set in stone either, so if you would structure the models in any other way, please tell!
first idea:
in the view you could query for today's ProductStatistic, than loop over the the queryset and add a variable ranking to every object and add that object to a list. Then just sort after ranking and pass the list to ur template.
second idea:
create a filed ranking (hidden for admin) and write the solution of ur formula each time the object is saved to the database by using a pre_save-signal. Now you can do ProductStatistic.objects.filter(date=today()).order_by('ranking')
Both ideas have pros&cons, but I like second idea more
edit as response to the comment
Use Idea 2
Write a view, where you filter like this: ProductStatistic.objects.filter(product= aProductObject, date__gte=startdate, date__lte=enddate)
loop over the queryset and do somthing like aProductObject.ranking+= qs_obj.ranking
pass a sorted list of the queryset to the template
Basically a combination of both ideas
edit to your own answer
Your solution isn't far away from what I suggested — but in sql-space.
But another solution:
Make a Hit-Model:
class Hit(models.Model):
date = models.DateTimeFiles(auto_now=True)
product = models.ForeignKey(Product)
purchased= models.BooleanField(default=False)
session = models.CharField(max_length=40)
in your view for displaying a product you check, if there is a Hit-object with the session, and object. if not, you save it
Hit(product=product,
date=datetime.datetime.now(),
session=request.session.session_key).save()
in your purchase view you get the Hit-object and set purchased=True
Now in your templates/DB-Tools you can do real statistics.
Of course it can generate a lot of DB-Objects over the time, so you should think about a good deletion-strategy (like sum the data after 3 month into another model MonthlyHitArchive)
If you think, that displaying this statistics would generate to much DB-Traffic, you should consider using some caching.
I solved this the way I didn't want to solve it. I added week_rank, month_rank and overall_rank to Product and then I just added the following to my ProductStatistic model.
def calculate_rank(self, days_ago=7, overall=False):
if overall:
return self._default_manager.all().extra(
select = {'rank': 'SUM(hits + (clicks * 2))'}
).values()[0]['rank']
else:
return self._default_manager.filter(
date__gte = datetime.today()-timedelta(days_ago),
date__lte = datetime.today()
).extra(
select = {'rank': 'SUM(hits + (clicks * 2))'}
).values()[0]['rank']
def save(self, *args, **kwargs):
super(ProductStatistic, self).save(*args, **kwargs)
t = Product.objects.get(pk=self.product.id)
t.week_rank = self.calculate_rank()
t.month_rank = self.calculate_rank(30)
t.overall_rank = self.calculate_rank(overall=True)
t.save()
I'll leave it unsolved if there is a better solution.