Serializing an SQL Function in Django REST - django

I'm using Django and Django Rest Framework to build an API.
I have a custom PostgreSQL function like this: myfunction(from_date, to_date)
It simply calculates a figure from records in the date range in reviews table.
How can I (safely!) take from_date and to_date from URL query parameters, pass them to the function, and return the (float) result via the API?
I have made a simple model to hold the float, like this:
class Result(models.Model):
result = models.FloatField(default=0)
class Meta:
managed = False
Alternatively, could I forgo the SQL function and implement this directly in Django/REST itself?
I already have a model for reviews. In the view for result can I pull reviews from a date range, calculate it, and return the value as a serialized result response?
As an end result, I want to be able to curl http://my.api/result?from=2019-03-01&to=2019-04-01 and get the result.
Thank you.

you can just simple filtering with lte and gte in your review Model and i am supposing date is a field of your Review model where you store record of every review. Then
Review.objects.filter(date__gte=from_date, date__lte=to_date)
Replace date with appropriate field name of your Review model And also with appropriate serializer.

Related

How to structure django models and query them with ease?

I am building a Django web App that will count the total number of persons entering and exiting a school library in a day, week and year and then save to DB.
The Web App uses a camera that is controlled by OpenCv to show live feed on frontend (I have successfully implemented this already).
My problem is:
How can I design and structure my models to store each data by day, week, month and year?
And how can I query them to display them on different Bar Charts using chart.js?
I haven't used chart.js before but I think I can answer the first part of your question.
Consider this model from one of my projects for a "post" that a user can make on my webapp.
class Post(models.Model):
slug = models.SlugField(unique=True)
title = models.CharField(max_length=100)
description = models.CharField(max_length=2200)
image = models.ImageField(upload_to=photo_path, blank=False, null=True)
timestamp = models.DateTimeField(auto_now_add=True)
Using a "DateTimeField" (or alternatively a "DateField") you can pretty easily store timestamp information which can be filtered using standard python Date or DateTime object comparisons. In my example, I'm storing image files and text information.
For your case you could simply create a new "Person" model where each person is given a timestamp (and whatever other info you might want to store) and then using django querying you can count how many people match certain datetime parameters.
Note the Django Docs (https://docs.djangoproject.com/en/3.1/ref/models/querysets/) recommend :
Don't use len() on QuerySets if all you want to do is determine the number of records in the set. It's much more efficient to handle a count at the database level, using SQL's SELECT COUNT(*), and Django provides a count() method for precisely this reason.
An example of how I'd approach your problem would be:
Models:
class Person(HabitModel):
timestamp = models.DateTimeField(auto_now_add=True)
#whatever extra data you want on each person walking by
#staticmethod
def get_number_of_people(start_timestamp, end_timestamp):
return Person.objects.filter(timestamp__gte=start_timestamp, timestamp__lt=end_timestamp)).count()
(Note the "__gte" and "__lt" are built-in for Django querying and imply [start_timestamp, end_timestamp) inclusive start time and exclusive endtime)
Now you should be able to store your data rather simply and quantify how many people objects were created in whatever timeframe you'd like!

How to replace foreign key by its associated value in django queryset

I'm fairly new to django and i would need your help!
I wrote an api/route view that query the database and return a JSON to my fetch function in my javascript.
Is there a way to query the database and got back a queryset with foreign key replaced by its associated value ?
I though i could use ModelName.objects.select_related( 'field ') but i didn't understand how it works.
If you have a solution or advices on better way to achieve the goal, Thanks in advance!
Context in pseudo-code:
// HTML //
Button onclick function get_list_of_data
// JS //
function get_list_of_data:
Fetch URL of route django
Convert response to JSON
iterating through JSON to fill HTLM div
// Django //
use ModelName.objects.filter( name = name ) to get list of data in Queryset
use serializers.serialize(json, ...") to get JSON from Queryset
return JsonResponse (json_query)
If I understood the problem well, when you serialize a model that has a ForeignKey field defined you get only the id value in JSON response but you would like to get the whole object (not only a number) returned.
The way to do that is to specifically write serializer for that ForeignKey model and then use it within the serializer od the model that you are trying to fetch.
You haven't provided any code, but here is some example that might help you:
class SecondModelSerializer(serializers.ModelSerializer):
class Meta:
model = SecondModel
fields = '__all__'
class FirstModelSerializer(serializers.ModelSerializer):
foreign_key_field = SecondModelSerializer()
class Meta:
model = FirstModel
fields = ('id', 'foreign_key_field', 'field1', 'field2')
Here in your FirstModelSerializer you specifically told Django to use SecondModelSerializer for your ForeignKey field (I named it foreign_key_field). This way Django will know how to serialize that field instead of returning only the id value.

Store user-defined filters in database

I need to allow users to create and store filters for one of my models. The only decent idea I came up with is something like this:
class MyModel(models.Model):
field1 = models.CharField()
field2 = models.CharField()
class MyModelFilter(models.Model):
owner = models.ForeignKey('User', on_delete=models.CASCADE, verbose_name=_('Filter owner'))
filter = models.TextField(_('JSON-defined filter'), blank=False)
So the filter field store a string like:
{"field1": "value1", "field2": "value2"}.
Then, somewhere in code:
filters = MyModelFilter.objects.filter(owner_id=owner_id)
querysets = [MyModel.objects.filter(**json.loads(filter)) for filter in filters]
result_queryset = reduce(lambda x, y: x|y, querysets)
This is not safe and I need to control available filter keys somehow. On the other hand, it presents full power of django queryset filters. For example, with this code I can filter related models.
So I wonder, is there any better approach to this problem, or maybe a 3rd-party library, that implements same functionality?
UPD:
reduce in code is for filtering with OR condition.
UPD2:
User-defined filters will be used by another part of system to filter newly added model instances, so I really need to store them on server-side somehow (not in cookies or something like that).
SOLUTION:
In the end, I used django-filter to generate filter form, then grabbing it's query data, converting in to json and saving it to the database.
After that, I could deserialize that field and use it in my FilterSet again. One problem that I couldn't solve in a normal way is testing single model in my FilterSet (when model in already fetched and I need to test, it it matches filter) so I ended up doing it manually (by checking each filter condition on model).
Are you sure this is actually what you want to do? Are your end users going to know what a filter is, or how to format the filter?
I suggest that you look into the Django-filter library (https://django-filter.readthedocs.io/).
It will enable you to create filters for your Django models, and then assist you with rendering the filters as forms in the UI.

Django rest framework: automatically create a url for each field of a model

I have large table of data (~30 Mb) that I converted into into a model in Django. Now I want to have access to that data through a REST API.
I've successfully installed the Django REST framework, but I'm looking for a way to automatically create a URL for each field in my model. My model has about 100 fields, and each field has about 100,000 entries.
If my model is named Sample,
models.py
class Sample(models.Model):
index = models.IntegerField(primary_key=True)
year = models.IntegerField(blank=True, null=True)
name = models.TextField(blank=True, null=True)
...97 more fields...
then I can access the whole model using Django REST framework like this:
urls.py
class SampleSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Sample
fields = ( **100 fields**)
class SampleViewSet(viewsets.ModelViewSet):
queryset = Sample.objects.all()
serializer_class = SampleSerializer
router = routers.DefaultRouter()
router.register(r'sample', SampleViewSet)
But of course my browser can't load all of that data in a reasonable amount of time. I could manually make a different class and URL for each field, but there must be a better way... I want to be able to go to my_site.com/sample/year (for example) and have it list all of the years in JSON format, or my_site.com/sample/name and list all the names, etc.
Please help me figure out how to do this, thanks!
You might be able to do that using a custom viewset route.
You have this:
class ModelViewSet(ModelViewSet):
#list_route()
def sample_field(self, request):
desired_field = request.data.get('field', None)
if not desired_field:
return response # pseudocode
values = Model.objects.all().values_list(desired_field, flat=True)
# serialize this for returning the response
return Response(json.dumps(values)) # this is an example, you might want to do something mode involved
You will be able to get this from the url:
/api/model/sample_field/?field=foo
This extra method on the viewset will create a new endpoint under the samples endpoint. Since it's a list_route, you can reach it using /sample_field.
So following your code, it would be:
mysite.com/sample/sample_field/?field='year'
for example.
There are many interesting details in your question, but with this sample I think you might able to achieve what you want.
Try to use pagination. You can do it in almost the same way as in you question. Pagination in django lets you divide the results into pages. You don't have to display all the entries in the same page. I think this is the best option for you.
Refer django documentation on pagination:
Pagination in django

Can Tastypie handle arbitrary arrays of objects?

I'm working on a project which requires REST API. I have tried Piston but it doesn't suit my requirement as it currently allows only 1 handler per model.
Tastypie seems to be a better alternative. However, I'm stuck with the following problem. My article class is displayed according to a complex rule such as ranking and date created. To enhance server performance, I created a dummy table which records the order of all the articles so that upon user requests, the complex ordering process will not be executed. Instead, the server checks for the orders of each article from the dummy table.
With Tastypie, a query set is required. However, because I want to use the orders recorded in the dummy table, I have to use a more complex code snippet to retrieve the data.
Is there any possibility that I can return an array of article objects and Tastypie can transform them into a proper JSON format.
What you need is extending the queryset in Meta. Assuming your articles table should be ordered by some additional data your queryset would be defined for example like that:
Meta:
queryset = Article.objects.extra(select={
'ordering': 'SELECT foo FROM bar'
},).order_by('ordering')
You have to define the additional fields in your resources:
ordering = field.IntegerField(attribute="ordering", default=0, readonly=True)
The additional field should now be returned with all other fields retrieved from your queryset. Note that if you define the fields attribute in your meta you also have to add the new field there.