How to "select distinct" with flask-restless - flask-restless

I'm new to flask-restless, and looking for a way to do a "SELECT DISTINCT" on a table. I've been reading the docs and found "Function evaluation". But I couldn't find how to put a function evaluation into a preprocessor or am I absolutely wrong?
Does someone know a way how to do that?

Function evaluation only return values of computational functions, for exemple count, max, avg. I don't think it is the good way to dig in.
You should probably go with a custom query embedded in your class, as showed in the Custom Query https://flask-restless.readthedocs.org/en/latest/customizing.html#custom-queries
from sqlalchemy import distinct
class Person(Base):
__tablename__ = 'person'
id = Column(Integer, primary_key=True)
name = Column(Unicode(50))
#classmethod
def query(cls):
return cls.query(func.distinct(Person.name))

You should use Custom queries.
Example:
class Employee(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.Unicode)
#classmethod
def get_unique_values(cls):
return db.session.query(func.distinct(Employee.name))

Related

django-REST: Nested relationships vs PrimaryKeyRelatedField

Is it better to use nested relationships or PrimaryKeyRelated field if you have lots of data?
I have a model with deep relationships.
For simplicity I did not add the colums.
Model:
Usecase:
User creates 1 Workoutplan with 2 Workouts and 3 WorkoutExercises.
User creates 6 Sets for each WorkoutExercise/Exercise.
User starts workout > new FinishedWorkout is created
User does first exercise and enters the used weights > new FinishedWorkoutExercise with FinishedSet is created
Question:
I want to track the progression for each workoutplan > workout > exercise.
So with time the user may have finished dozens of workouts therefore hundreds if sets are already in the database.
If I now use nested Relationships I may load a lot of data I don't need.
But if I use PrimaryKeyRelatedFields I have to load all the data I need separately which means more effort in my frontend.
Which method is preferred in such a situation?
Edit:
If I use PrimaryKeyRelatedFields how do I distinguish if e.g. Workouts in Workoutplan is an array with primary keys or an array with the loaded objects?
If you use PrimaryKeyRelatedField, you'll have a big overload to request the the necessary data in frontend
In your case, I would create specific serializers with the fields you want (using Meta.fields attribute). So, you won't load unecessary data and the frontend won't need to request more data from backend.
I can write a sample code, if you need more details.
I'll get to the question regarding serializers in a second, but first of all and for clarification. What is the purpose of having duplicate models as Workout/Finished Workout, Set/Finished Set,...?
Why not...
class Workout(models.Model):
#...stuff...
finished = models.DateTimeField(null=True, blank=True)
#...more stuff...
Then you can just set a finished date on a workout when it's done.
Now, regarding the question. I would suggest you think about user interactions. What parts of the front-end are you trying to populate? How is the data related and how would the user access it?
You should think about what parameters you're querying DRF with. You can send a date and expect workouts finished on a specific day:
// This example is done in Angular, but you get the point...
var date= {
'day':'24',
'month':'10',
'year':'2015'
};
API.finishedWorkout.query(date).$promise
.then(function(workouts){
//...workouts is an array of workout objects...
});
Viewset...
class FinishedWorkoutViewset(viewsets.GenericAPIView,mixins.ListModelMixin):
serializer_class = FinishedWorkOutSerializer
queryset = Workout.objects.all()
def list(self, request):
user = self.request.user
day = self.data['day'];
month = self.data['month'];
year = self.data['year'];
queryset = self.filter_queryset(self.get_queryset().filter(finished__date=datetime.date(year,month,day)).filter(user=user))
page = self.paginate_queryset(queryset)
serializer = self.get_serializer(queryset, many=True)
return response.Response(serializer.data)
And then your FinishedWorkoutSerializer can just have whatever fields you want for that specific type of query.
This leaves you with a bunch of very specific URLs, which isn't all that great, but you can use specific serializers for those interactions and you're also open to dynamically changing the filter, depending on what paramaters are in self.data.
There is also a chance that you may want to filter differently depending what method is being called, say you want to list only active exercises, but if a user queries a specific exercise, you want him to have access to it (note that the Exercise object should have a models.BooleanField attribute called "active").
class ExerciseViewset(viewsets.GenericViewSet, mixins.RetrieveModelMixin, mixins.ListModelMixin):
serializer_class = ExerciseSerializer
queryset = Exercise.objects.all()
def list(self, request):
queryset = self.filter_queryset(self.get_queryset().filter(active=True))
page = self.paginate_queryset(queryset)
serializer = self.get_serializer(queryset, many=True)
return response.Response(serializer.data)
Now you have different objects show up on the same URL, depending on the action. It's a bit closer to what you need, but you're still using the same serializer, so if you need a huge nested object on retrieve(), you're also gonna get a bunch of them when you list().
In order to keep lists short and details nested, you need to use different serializers.
Let's say you want to only send exercises' pk and name attributes when they are listed, but whenever an exercise is queried, you wan't to send along all related "Set" objects ordered inside an array of "WorkoutSets"...
# Taken from an SO answer on an old question...
class MultiSerializerViewSet(viewsets.GenericViewSet):
serializers = {
'default': None,
}
def get_serializer_class(self):
return self.serializers.get(self.action, self.serializers['default'])
class ExerciseViewset(MultiSerializerViewSet, mixins.RetrieveModelMixin, mixins.ListModelMixin):
queryset = Exercise.objects.all()
serializers = {
'default': SimpleExerciseSerializer,
'retrieve': DetailedExerciseSerializer
}
Then your serializers.py could look a bit like...
#------------------Exercise
#--------------------------Simple List
class SimpleExerciseSerializer(serializers.ModelSerializer):
class Meta:
model Exercise
fields = ('pk','name')
#--------------------------Detailed Retrieve
class ExerciseWorkoutExerciseSetSerializer(serializers.ModelSerializer):
class Meta:
model Set
fields = ('pk','name','description')
class ExerciseWorkoutExerciseSerializer(serializers.ModelSerializer):
set_set = ExerciseWorkoutExerciseSetSerializer(many=True)
class Meta:
model WorkoutExercise
fields = ('pk','set_set')
class DetailedExerciseSerializer(serializers.ModelSerializer):
workoutExercise_set = exerciseWorkoutExerciseSerializer(many=True)
class Meta:
model Exercise
fields = ('pk','name','workoutExercise_set')
I'm just throwing around use cases and attributes that probably make no sense in your model, but I hope this is helpfull.
P.S.; Check out how Java I got in the end there :p "ExcerciseServiceExcersiceBeanWorkoutFactoryFactoryFactory"

Dealing with unique constraints that should be replaced in django_rest_framework

tl;dr: How can I ignore (turn off) a unique constraint in django_rest_framework Create calls with a ListCreateAPIView, because I'm going to deal with it manually in the perform_create method?
Im using a third party library django-push-notifications. It has a nice model for APNSDevice (apple push notification service device) that has a unique constraint on a registration_id field.
My problem is that sometimes I want to manually delete old values in the table that have the registration ID, so that I can insert a new value. I'd like to use this serializer:
class APNSDeviceSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = APNSDevice
fields = ('name', 'active', 'device_id', 'registration_id')
along with this code for PUT
class MyAppleDevices(generics.ListCreateAPIView):
permission_classes = (permissions.IsAuthenticated,)
serializer_class = APNSDeviceSerializer
model = APNSDevice
def get_queryset(self):
return APNSDevice.objects.filter(user = self.request.user)
def perform_create(self, serializer):
print "Looking for old devices with registration id "+str(self.request.registration_id)
oldDevices = APNSDevice.objects.filter(registration_id = self.request.registration_id)
for oldDevice in oldDevices:
oldDevice.delete()
apnsDevice = serializer.save(user=self.request.user)
In other words, I'm trying to manually delete other entries that have the unique constraint in this particular PUT, so that I can insert the new one without violating the unique constraint. The problem is the validator runs before the perform_create method is called, and I can't figure out how to turn off the validator's unique constraint. I tried adding this to the Serializer
def get_validation_exclusions(self, instance = None):
exclusions = super(APNSDeviceSerializer, self).get_validation_exclusions(instance)
return exclusions + ['registration_id']
but it doesn't help so obviously I have no clue even though I've been pouring through the documentation and Stack Overflow posts. Any help appreciated, thanks. I suppose as a last resort I could remove the unique constraint from the model, but it is a valid constraint so I'd rather leave it in.
I found this question because I had this exact problem with that exact library. You can get around it by subclassing the serializer and manually overriding the field definition:
class APNSDeviceSerializerWithNonUniqueRegistrationId(APNSDeviceSerializer):
registration_id = serializers.CharField(min_length=64, max_length=64)
class Meta(APNSDeviceSerializer.Meta):
fields = ("name", "registration_id", "device_id", "active", "date_created")
Then, if you're using django-push-notifications, you'll also need to override the ViewSet that uses that serializer:
class APNSDeviceAuthorizedViewSetWithNonUniqueRegistrationId(AuthorizedMixin, APNSDeviceViewSet):
"""
The out of the box viewset/serializer combo require the registration ID to be unique and won't
allow setting a registration ID to a new user (which is useful if we have potentially more than
one account on a device.)
"""
serializer_class = APNSDeviceSerializerWithNonUniqueRegistrationId
def perform_create(self, serializer):
if self.request.user.is_authenticated():
try:
existing_registration = APNSDevice.objects.get(
registration_id=serializer.validated_data['registration_id'])
existing_registration.delete()
except APNSDevice.DoesNotExist:
pass
serializer.save(user=self.request.user)
return super(DeviceViewSetMixin, self).perform_create(serializer)

how to check if user has permission to delete objects in django

I am trying to write a view method that responds to an AJAX request to delete an entry. I want to check if the end user is the actual author of the Entry before deleting that Entry. Does my "if" statement accomplish this?
VIEWS.PY
latest_entries=Entry.objects.order_by('-pub_date')[:16]
#login_required
def delete_object(request):
if request.is_ajax():
object_name = request.POST.get('entryname')
targetobject = Entry.objects.get(author=object_name)
if request.user = targetobject.author:
targetobject.delete()
return HttpResponseRedirect('/storefront/')
MODELS
Class Entry(models.Model):
author = models.CharField(max_length=30)
subject = models.CharField(max_length=30)
description = models.CharField(max_length=30)
You're almost there. request.user is an instance of django.utils.SimpleLazyObject, so you won't be able to do an == comparison of request.user to a CharField, of which the value is a string under the covers.
You need to do something like:
if request.user.username == targetobject.author:
targetobject.delete()
or just use whatever field from the User object is synonymous with Entry.author.
I'd say your model is wrong. author should be a ForeignKey to the auth.User model. Then your comparison would work (with the change to ==), and there are other benefits too in terms of grouping and querying by user attributes.

Adding filtering changes ordering

I have a ModelViewSet that I want to add filtering to. My simple model looks like
class Article(models.Model):
date = = models.DateField()
language = models.CharField(max_length=10)
class Meta:
ordering = ['-date']
And the ModelViewSet (read only):
class ArticleViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
Articles on the API are now ordered by date descending as I would expect. Now I wich to allow filtering on language. I've set the filter backend to DjangoFilterBackend in settings.py. My updated ModelViewSet now looks like:
class ArticleViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
filter_fields = ['language']
This changes the ordering to language ASC. Adding order_by('-date') to queryset does not change anything. Adding ordering = ('-date', ) does not change anything. => How do I specify both filtering and ordering (or simply use default ordering while allowing filtering)?
EDIT:
Current functionality seems to come from AutoFilterSet created in Rest Framework by default:
https://github.com/tomchristie/django-rest-framework/blob/822eb39599b248c68573c3095639a831ab6df99a/rest_framework/filters.py#L53
... where order_by=True and the handing of this in django-filter get_ordering_field here: https://github.com/alex/django-filter/blob/d88b98dd2b70551deb9c128b209fcf783b325acc/django_filters/filterset.py#L325
=> Seems I have to create a FilterSet class:
class LanguageFilter(django_filters.FilterSet):
class Meta:
model = Article
fields = ['language']
order_by = model()._meta.ordering
class ArticleViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
filter_class = LanguageFilter
Does this look correct? Seems a bit "much"/verbose to retain default ordering.
Rather than implementing your own FilterSet, you can instead just add an OrderingFilter, specifying an ordering = ['-date'] or better: ordering = Article._meta.ordering on your view, to restore the lost (default) ordering. This would also allow your users to use an ordering query parameter to override your default ordering of results.
Note that this issue has been resolved in master... https://github.com/tomchristie/django-rest-framework/pull/1836 and is due to be released in version 2.4.3.
Good question.
Is ok to apply an ordering filter in conjuction with a Django-Filter but I think is not right that a Filter Backend applies a reorder function.
In my case I have to cache my random queryset and so i can't use Django-Filter anymore, even if I'm not filtering at the page's first asyncronous call.

How to remember results from querying SQLAlchemy relations (to implement caching)?

Suppose I have a mapped class Article. It has a relation category that does a query each time I access it (article.category would issue a query to get the category or article).
How do I proxy the article.category call so that the result is queried from the database, then remembered and then returned?
Thanks, Boda Cydo.
Does SA really issue a query every time you access the relation in the same session? IMO, it should not happen, as the result should get cached automatically within the session.
In order to check that no SQL is issued, just turn on logging and see for yourself:
metadata.bind.echo = 'debug'
c = session.query(Child).first() # issues SELECT on your child table(s)
print "child: ", c
print "c.parent: ", c.parent # issues SELECT on your parent table, then prints
print "c.parent: ", c.parent # just prints (no SQL)
print "c.parent: ", c.parent # just prints (no SQL)
Shall your code work otherwise by default, please provide code snippet.
In case you really just need to cache the result, see below (very similar solution to another question you posted):
class MyChild(Base):
__tablename__ = 'MyChild'
id = Column(Integer, primary_key=True)
parent = relation('Parent')
# ... other mapped properties
def __init__(self):
_parent_cached = None
#property
def parent_cached(self):
if self._parent_cached is None:
self._parent_cached = self.parent
But in order to have the result when your object is detached from the session you must call this property before detaching. (Also it does not handle situation when the parent is None. Do you always have parent?).
The option with eager load is simplier and once you load the object, you should have the relation loaded already (key is to have lazy=False):
class MyChild(Base):
__tablename__ = 'MyChild'
id = Column(Integer, primary_key=True)
parent = relation('Parent', lazy=False)
# ... other mapped properties
...

Resources