My models.py looks like this :
class Prescription(models.Model):
date_prescribed = models.DateTimeField()
doctor = models.ForeignKey(Doctor)
pharmacy = models.ForeignKey(Pharmacy)
class Doctor(models.Model):
name = models.CharField(max_length=150)
age = models.PositiveSmallIntegerField()
class Pharmacy(models.Model):
name = models.CharField(max_length=150)
status = models.CharField()
In my views,I want the count of prescriptions grouped by month.I am using Django rest framework.My views.py is as follows:
class PrescriptionTrendListView(generics.ListAPIView):
queryset = Prescription.objects.all()
serializer_class = LineGraphSerializer
def get_queryset(self):
end_date = timezone.now()
start_date = end_date - relativedelta(months=6)
truncate_date = connection.ops.date_trunc_sql('month', 'date_prescribed')
qs = super().get_queryset.extra(select={'month': truncate_date})
return qs.filter(date_prescribed__range=(start_date, end_date)
).annotate(pk_count=Count('pk')).order_by('month')
def get(self, request, *args, **kwargs):
graph_data = self.get_queryset().values('pk_count', 'month')
serializer = self.get_serializer(data=graph_data, many=True)
return Response(serializer.data)
However,the error I get when I run this is :
I am assuming this has something to do with new style classes in Python 3.0.But that is all i know.Any help on how to fix this on 2.x ?
Your super() call in get_queryset is missing the type and self arguments.
class PrescriptionTrendListView(generics.ListAPIView):
queryset = Prescription.objects.all()
serializer_class = LineGraphSerializer
def get_queryset(self):
end_date = timezone.now()
start_date = end_date - relativedelta(months=6)
truncate_date = connection.ops.date_trunc_sql('month', 'date_prescribed')
# Bug was here
qs = super(PrescriptionTrendListView, self).get_queryset.extra(select={'month': truncate_date})
return qs.filter(date_prescribed__range=(start_date, end_date)
).annotate(pk_count=Count('pk')).order_by('month')
def get(self, request, *args, **kwargs):
graph_data = self.get_queryset().values('pk_count', 'month')
serializer = self.get_serializer(data=graph_data, many=True)
return Response(serializer.data)
https://docs.python.org/2/library/functions.html#super
In Python 2, syntax for super is this:
class MyClass(Base):
def func(self, *args, **kwargs):
super(MyClass, self).func(*args, **kwargs)
This is really stupid but I suspect it's because you're referencing Doctor and Pharmacy before you declare them. Try this instead:
class Doctor(models.Model):
name = models.CharField(max_length=150)
age = models.PositiveSmallIntegerField()
class Pharmacy(models.Model):
name = models.CharField(max_length=150)
status = models.CharField()
class Prescription(models.Model):
date_prescribed = models.DateTimeField()
doctor = models.ForeignKey(Doctor)
pharmacy = models.ForeignKey(Pharmacy)
I looked at some of my old Django on Python 2.x and your models look fine, just that seems to be an issue maybe(?)
Related
I perform request http://167.71.57.114/api2/workout-exercises/3
I want to receive data about WorkoutExercise object number 3 (detail view)
Got AttributeError when attempting to get a value for field description on serializer ExerciseSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the RelatedManager instance.
Original exception text was: 'RelatedManager' object has no attribute 'description'.
serializers.py
class WorkoutExerciseSerializer(serializers.ModelSerializer):
exercises = ExerciseSerializer()
class Meta:
model = WorkoutExercise
fields = ('week', 'exercises')
views.py
class WorkoutExerciseViewSet(viewsets.ModelViewSet):
queryset = WorkoutExercise.objects.all()
serializer_class = WorkoutExerciseSerializer
http_method_names = ['get', 'post']
models.py
class WorkoutExercise(models.Model):
workout_program = models.ForeignKey(WorkoutProgram, on_delete=models.CASCADE, related_name='workout_exercises')
week = models.PositiveIntegerField(default=1)
day = models.PositiveIntegerField(default=1)
order = models.PositiveIntegerField(default=1)
def save(self, *args, **kwargs):
if not self.pk:
last_order = WorkoutExercise.objects.all().aggregate(largest=models.Max('order'))['largest']
if last_order is not None:
self.order = last_order + 1
return super(WorkoutExercise, self).save(*args, **kwargs)
def get_workout_programs(self):
return self.workout_program.name
def get_exercises(self):
pass
def __str__(self):
return self.workout_program.name
class Meta:
ordering = ('week', 'day')
Based on the fact that exercises is plural, and RelatedManager error, it means that there are multiple Exercises, so you need to serialize these with a many=True parameter:
class WorkoutExerciseSerializer(serializers.ModelSerializer):
exercises = ExerciseSerializer(many=True)
class Meta:
model = WorkoutExercise
fields = ('week', 'exercises')
Can we pass the URL queryset parameter into serizalizer to have filitering/calculation?
here is my endpoint: ?start_date=20210312&end_date=20210317
herer is my viewset:
class BrandChartsViewSet(ModelViewSet):
serializer_class = BrandChartsSerializer
pagination_class = BrandChartsPagination
queryset = Brand.objects.all()
def get_queryset(self):
start_date = self.request.query_params.get('start_date',None)
end_date = self.request.query_params.get('end_date',None)
if start_date is None:
raise InvalidInputError()
if end_date is None:
raise InvalidInputError()
start_date_obj = datetime.strptime(start_date,'%Y%m%d')
end_date_obj = datetime.strptime(end_date,'%Y%m%d')
serializer = BrandChartsSerializer(start_date_obj,end_date_obj)
queryset = Brand.objects.all()
return queryset
here is my serizalizer:
class BrandChartsSerializer(serializers.ModelSerializer):
rankings = serializers.SerializerMethodField()
instagram = IGSerializer(allow_null=True)
facebook = FBSerializer(allow_null=True)
hashtags = BrandHashtagSerializer(allow_null=True, many=True)
# rename the json field
brand_uid = serializers.IntegerField(source='id')
brand_name = serializers.CharField(source='name')
origin = serializers.CharField(source="country")
class Meta:
model = Brand
fields = [
"brand_uid",
"brand_name",
"origin",
"rankings",
"instagram",
"facebook",
"hashtags",
]
def get_rankings(self, instance):
rank = instance.rankings.all().filter(created__gte=start_date_obj,
created__lte=end_date_obj
).order_by('created')
return BrandRankSerializer(rank, allow_null=True, many=True).data
and it will have Unresolved reference 'start_date_obj' in get_rankings, however I tried to passed the parameter into serizalizer by class BrandChartsSerializer(serializers.ModelSerializer,start_date_obj,end_date_obj)
It still giving error.
Due to my function design, I think it could not use DRF filter to handle it, could I just pass the parameter to serizalizer to do filitering? (I need to filter the rankings model in range and calculate the sum than return)
We can make use of context in such cases.
Try this.
Update the get_rankings method.
def get_rankings(self, instance):
start_date_obj = self.context.get("request").query_params.get("start_date")
end_date_obj = self.context.get("request").query_params.get("end_date")
rank = instance.rankings.all().filter(created__gte=start_date_obj,
created__lte=end_date_obj
).order_by('created')
return BrandRankSerializer(rank, allow_null=True, many=True).data
class PlayerList(models.Model):
name = models.CharField(max_length=300)
position = models.CharField(max_length=200)
h_code = models.ForeignKey(HList, related_name="h_code", on_delete=models.CASCADE)
d_code = models.CharField(primary_key=True, max_length = 200, editable=False)
Serializers.py
class PlayerSerializer(serializers.ModelSerializer):
class Meta:
fields = ["name", "position", "h_code", "d_code"]
model = PlayerList
view.py
class PostPlayer(generics.ListCreateAPIView):
queryset = PlayerList.objects.all().order_by('-d_code')
serializer_class = PlayerListSerializer
def get(self, request, *args, **kwargs):
d_code = request.data.get('h_code') + 'test'
print(d_code)
print(d_code) :
h000001test
When entering a value through api, I want to implement that the entered value is changed to another value in view.py and saved in the db.
I want to save d_code processed by def get in db.
I don't know what to do. Can you please let me know?
There is no answer, so I will post again.
You can override your views perform_create() method, as stated in the docs (you might have to scroll a little, its in the Save and deletion hooks section:
class PostPlayer(generics.ListCreateAPIView):
queryset = PlayerList.objects.all().order_by('-d_code')
serializer_class = PlayerListSerializer
def get(self, request, *args, **kwargs):
d_code = request.data.get('h_code') + 'test'
print(d_code)
...
...
def perform_create(self, serializer):
d_code = request.data.get('h_code') + 'test'
#do something with d_code
new_code = d_code + 'someSampleValue'
serializer.save(d_code=new_code)
I have the two models, Fillup and Car, and the Fillup model has a Foreign key (for recording times you fill up your car with gas, for example), and in the form to create a new Fillup, I want to limit the dropdown for the Car field to only Cars associated with the current user, but right now it's showing all users cars. I've seen a couple solutions that involve passing the request into the form from the view but I can't figure out how to do it using the Class Based Views I currently have set up. Here's my code:
models.py
class Fillup(models.Model):
username = models.ForeignKey(User,on_delete=models.CASCADE)
date = models.DateField(default=date.today)
price_per_gallon = models.FloatField()
trip_distance = models.FloatField()
gallons = models.FloatField()
car = models.ForeignKey('Car',on_delete=models.CASCADE)
#property
def total_sale(self):
return round(self.price_per_gallon*self.gallons, 2)
#property
def mpg(self):
return round(self.trip_distance/self.gallons, 4)
class Car(models.Model):
username = models.ForeignKey(User,on_delete=models.CASCADE)
name = models.CharField(max_length=25)
make = models.CharField(max_length=25)
model = models.CharField(max_length=25)
model_year = models.IntegerField(choices=MODEL_YEARS)
status = models.BooleanField(choices=STATUS)
def __str__(self):
return self.name
views.py
class FillupListView(ListView):
model = Fillup
context_object_name = 'fillup_list'
ordering = ['-date']
# NOT USING THIS YET
# def get_queryset(self):
# return Fillup.objects.filter(user=self.request.user)
class CarListView(ListView):
model = Car
ordering = ['name']
class NewFillup(LoginRequiredMixin,CreateView):
model = Fillup
fields = ('date', 'price_per_gallon', 'trip_distance', 'gallons', 'car')
redirect_field_name = 'fillup_list'
def form_valid(self, form):
form.instance.username = self.request.user
return super().form_valid(form)
class NewCar(LoginRequiredMixin,CreateView):
model = Car
fields = ('name', 'make', 'model', 'model_year', 'status')
redirect_field_name = 'car_list'
def form_valid(self, form):
form.instance.username = self.request.user
return super().form_valid(form)
forms.py
class FillupForm(forms.ModelForm):
def __init__(self, user, *args, **kwargs):
super(FillupForm,self).__init__(*args, **kwargs)
self.fields['car'].queryset = Car.objects.filter(username=user)
class Meta():
model = Fillup
fields = ('date', 'price_per_gallon', 'trip_distance', 'gallons', 'car')
class CarForm(forms.ModelForm):
class Meta():
model = Car
fields = ('name', 'make', 'model', 'model_year', 'status')
The overwriting of the init method in FillupForm was just one of the things I tried to get this to work, adapted from another Stackoverflow answer, but it didn't seem to have any effect. Any advice/examples to get this working would be appreciated! And let me know if I should supply any more pieces of my code
I ended up getting my answer to this from r/djangolearning on Reddit.
I needed to add the following to both of my CreateViews:
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
They also pointed out that I needed to replace the fields=('blah blah blah') on both CreateViews with form_class=forms.Fillup/Car
I hope this helps someone with the same issue as me!
You can do something like this in the init method.
cars = Car.objects.filter(username=user)
self.fields['car'].autocomplete = False
self.fields['car'].queryset = users
Hope this helps.
I am using ModelForm to allow multiple rows edit at the same time. It is a very simple form that has series of yes_no columns. The model looks like:
models.py
class Yn(models.Model):
yn_id = models.IntegerField(primary_key=True)
description = models.CharField(max_length=30)
def __str__(self):
return ' '.join([
self.description,
])
class Meta:
managed = False
db_table = 'yn'
class Invoice(models.Model):
description = models.CharField(max_length=50)
invoice_date = models.DateTimeField()
...
invoice_sent_yn = models.ForeignKey('Yn', models.DO_NOTHING, db_column='invoice_sent_yn', related_name="invoice_sent_yn")
confirm_receipt_yn = models.ForeignKey('Yn', models.DO_NOTHING, db_column='confirm_receipt_yn', related_name="confirm_receipt_yn")
paid_yn = models.ForeignKey('Yn', models.DO_NOTHING, db_column='paid_yn', related_name="paid_yn")
forms.py
class InvoiceGridEdit(ModelForm):
model = Invoice
fields = ['description','invoice_date','invoice_sent_yn', 'confirm_receipt_yn', 'paid_yn']
def __init__(self, *args, **kwargs):
super(JurisGridEditForm, self).__init__(*args, **kwargs)
...
...
InvoiceFormSet = modelformset_factory(models.Invoice, form=InvoiceGridEdit)
views.py
class InvoiceUpdateGrid(CreateView):
template_name = "/invoice_update_grid.html"
model = Invoice
form_class = InvoviceViewEditForm
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return redirect("account_login")
def get(self, request, *args, *kwargs):
self.object = None
customer_number = self.request.GET.get('customer_number')
invoice_form = InvoiceFormSet(queryset=Invoice.objects.filter(customer_number = customer_number)
return self.render_to_response(self.get_context_data(invoice_form=invoice_form))
def post(self, request, *args, **kwargs):
self.object = None
invoice_form = InvoiceFormSet(self.request.POST)
if (invoice_form.is_valid()):
return self.form_valid(invoice_form)
else:
return self.form_invalid(invoice_form)
def form_valid(self, invoice_form):
...
...
invoice_form.save()
return redirect("customer_list")
def form_invalid(self, invoice_form):
return self.render_to_response(self.get_context_data(invoice_form=invoice_form))
The forms works fine, get and post works, except, it takes a while (~30 sec) to retrieve and update. Using django-debug-toolbar, it looks like the yn columns retrieve separately for each column for each row (~2k rows). The Yn table only has 3 rows -1 - Unknown, 0 - No, 1 - Yes.
I tried to search for work around to stop the craziness of Django hitting the DB 900 times per retrieval. I found something about caching but I have no idea how to do it.
Thanks in advance.