rest_framework custom order_by - django-rest-framework

let's see if I can ask the questions correctly?
model.py
class Point(models.Model):
name = models.CharField(
"nome del sistema",
max_length=128,
)
x = models.FloatField(
"cordinata spaziale x",
)
y = models.FloatField(
"cordinata spaziale y",
)
z = models.FloatField(
"cordinata spaziale z",
)
distance = float(0)
def __str__(self) -> str:
return f"{self.name}"
#property
def get_distance(self):
return self.distance
#get_distance.setter
def get_distance(self, point):
"""
ritorna la distanza che ce tra due sistemi
"""
ad = float((point.x-self.x) if point.x > self.x else (self.x-point.x))
bc = float((point.y-self.y) if point.y > self.y else (self.y- point.y))
af = float((point.z-self.z) if point.z > self.z else (self.z-point.z))
return (pow(ad,2)+pow(bc,2)+pow(af,2))**(1/2)
class Meta:
verbose_name = "point"
verbose_name_plural = "points"
in the particular model there are two defs which calculate, save and return the distance with respect to the point we pass them
wenws.py
class PointViewset(viewsets.ModelViewSet):
"""
modelo generico per un systema
"""
queryset = Point.objects.all()
serializer_class = PointSerializers
filterset_class = PointFilter
in the wenws not that much particular to explain and base base the only thing we have to say and that as filters I use 'django_filters'
filters.py
import django_filters as filters
import Point
class CustomOrderFilter(filters.CharFilter):
def filter(self, qs:QuerySet, value):
if value in ([], (), {}, '', None):
return qs
try:
base = qs.get(name=value)
for point in qs:
point.get_distance = base
qs = sorted(qs, key= lambda x: x.get_distance)
except Point.DoesNotExist:
qs = qs.none()
return qs
class PointFilter(filters.rest_framework.FilterSet):
security = filters.ChoiceFilter(choices=security_choices
point= CustomCharFilter(
label = "point"
)
class Meta:
model = Point
fields = {
'name':['exact'],
}
now the complicated thing with 'CustomCharFilter' I pass in the http request the name of the system which then returns to me in the filter as value after I check that it is not empty and I start with returning the point that I have passed with base = qs.get ( name = value)
to then calculate and save the distance for each point with point.get_distance = base '' on the inside of the for, at the end I reorder the QuerySet with qs = sorted (qs, key = lambda x: x.get_distance) '' the problem that both with this way and with another that I have tried the QuerySet it 'transforms' into a list and this does not suit me since I have to return a QuerySet in the order of here I want. I don't know how to do otherwise, since order_by I can't use it since the distance is not inside the database
can someone help me?

So the problem is that you want to filter from a python function which cant be done in a query, as they only speak SQL.
The easy slow solution is to do the filtering in python, this might work if there are just a few Points.
points = list(Point.objects.all())
points.sort(cmp=comparison_function)
The actual real solution is to port that math to Djangos ORM and annotate your queryset with the distance to a given point, this is quite an advanced query, If you tell us your database server you use we can probably help you with that too.
ps. There is an abs() function in python to get absolute value instead of the if/else in get_distance

Related

Increasing the count field of model whenever a search is applied

I have a search api
class SearchViewSet(RetrieveModelViewSet):
serializer_class = ArticleSerializer
queryset = Article.objects.all()
query = self.request.query_params.get("query")
final_queryset = search(query,queryset,#some logic)
#logic to generate serialiser and return serialiser.data
serialiser = self.get_serializer(final_ueryset, many=True)
search function returns a list of articles i.e
type(final_queryset) is List
And. I wan't to return the articles order_by('count) as well.
Now I wan't to increase the count of top 3 articles from the final_queryset is there a way of doing this.
Add created_at field in your model, then use this field to find top 3 item or latest 3 items like this, and then increase count
queryset = Article.objects.order_by('-created_at')[:3] # it will return a queryset of latest 3 items
for article in queryset:
article.count += 1
article.save()
serializer = ArticleSerializer(article)
Note: I only shared the logic part here, and i can't test it in my local, but it should work and help you to get the idea. Use it on your need.
Figured out a way for doing this
Just simply iterate on the list and increment the count of the objects in the list
class SearchViewSet(RetrieveModelViewSet):
serializer_class = ArticleSerializer
queryset = Article.objects.all()
query = self.request.query_params.get("query")
final_queryset = search(query,queryset,#some logic)
# to increment the count of top 3 entity_aliases
for instance in final_queryset[:3]:
instance.count += 1
instance.save()
#logic to generate serialiser and return serialiser.data
serialiser = self.get_serializer(final_ueryset, many=True)

queryset filter between 2 values whit foreing keys?

I have problems with the following queryset, I probe it in django shell and it returns an empty list.
The situation is that I'm occupying a lforeing key, I did the same exercise with the model "Tarifa_Sem" and returns the value without any problem, just replace the F ('') by a variable x = 1000
The situation is that the table of the model "Tarifa_Sem" is only for consultation.
Where I am going to manage and save the response of the queryset is in the "Calculadora_isr" model
Model 1
class Tarifa_Sem(models.Model):
limite_inferior_isr = models.DecimalField(max_digits=10, decimal_places=2)
limite_inferior_subsidio = models.DecimalField(max_digits=10, decimal_places=2)
limite_superior = models.DecimalField(max_digits=10, decimal_places=2)
Model 2
class Calculadora_isr(models.Model):
tarifa = models.ForeignKey(Tarifa_Sem, on_delete=models.CASCADE, blank=True)
base_gravada = models.DecimalField(max_digits=10, decimal_places=2, blank=True)
limite_inf_calculo = models.DecimalField(max_digits=10, decimal_places=2, blank=True)
Queryset and save()
def limite_inferior(self):
queryset = Calculadora_isr.objects.filter(tarifa__limite_superior__gte=F('base_gravada'),tarifa__limite_inferior_isr__lte=F('base_gravada')).distinct().values('tarifa__limite_inferior_isr')
return queryset
def save(self):
self.limite_inf_calculo = self.limite_inferior
super (Calculadora_isr, self).save()
In the shell of django the list appears empty.
>>> queryset = Calculadora_isr.objects.filter(tarifa__limite_superior__gte=F('base_gravada'),tarifa__limite_inferior_isr__lte=F('base_gravada')).distinct().values('tarifa__limite_inferior_isr')
And in the admin when I give him save he tells me:
conversion from method to Decimal is not supported
thanks for the support
I finally found the solution.
To solve the problem of passing the "base_gravada" field, use another variable that returns all the values ​​of "base_gravada"
qs1 = Calculadora_isr.objects.values_list('base_gravada')
And in my second query, use the variable qs1:
qs2 = Tarifa_Sem.objects.filter(limite_superior__gte=qs1,limite_inferior_isr__lte=qs1).distinct().values('limite_inferior_isr')

How can I create a complete_name field in a custom module for a custom hierarchy like used on product categories in Odoo?

I'm trying to create a field “complete_name” that displays a hierarchy name similar to whats done on the product categories grid but I can't seem to get it to work. It just puts Odoo in an endless loading screen when I access the relevant view using the new field "complete_name".
I have tried to copy the code used in addons/product/product.py and migrate to work with Odoo 9 API by using compute instead of .function type but it did not work.
Can someone help me understand whats wrong? Below is my model class which works fine without the complete_name field in my view.
class cb_public_catalog_category( models.Model ):
_name = "cb.public.catalog.category"
_parent_store = True
parent_left = newFields.Integer( index = True )
parent_right = newFields.Integer( index = True )
name = newFields.Char( string = 'Category Name' )
child_id = newFields.One2many( 'catalog.category', 'parent_id', string = 'Child Categories' )
complete_name = newFields.Char( compute = '_name_get_fnc', string = 'Name' )
def _name_get_fnc( self ):
res = self.name_get( self )
return dict( res )
Your compute function is supposed to define the value of an attribute of your class, not return a value. Ensure the value you are assigning complete_name is a string.
Also name_get() returns a tuple. I am not sure if you really want a string representation of this tuple or just the actual name value.
Try this
def _name_get_fnc( self ):
self.complete_name = self.name_get()[1]
If you really want what is returned by name_get() then try this.
def _name_get_fnc( self ):
self.complete_name = str(self.name_get())
If you are still having issues I would incorporate some logging to get a better idea of what you are setting the value of complete_name to.
import logging
_logger = logging.getLogger(__name__)
def _name_get_fnc( self ):
_logger.info("COMPUTING COMPLETE NAME")
_logger.info("COMPLETE NAME: " + str(self.name_get()))
self.complete_name = self.name_get()
If this does not make it apparent what the issue is you could always try statically assigning it a value in the off chance that there is a problem with your view.
def _name_get_fnc( self ):
self.complete_name = "TEST COMPLETE NAME"
After further review I think I have the answer to my own question. It turns out as with a lot of things its very simple.
Simply use "_inherit" and inherit the product.category
model. This gives access to all the functions and fields
of product.category including the complete_name field
and computes the name from my custom model data. I was
able to remove my _name_get_func and just use the inherited
function.
The final model definition is below. Once this
update was complete I was able to add a "complete_name" field
to my view and the results were as desired!
class cb_public_catalog_category( models.Model ):
_name = "cb.public.catalog.category"
_inherit = 'product.category'
_parent_store = True
parent_left = newFields.Integer( index = True )
parent_right = newFields.Integer( index = True )
name = newFields.Char( string = 'Category Name' )
child_id = newFields.One2many( 'catalog.category', 'parent_id', string = 'Child Categories' )

djangorestframework-gis order by distance to point

In djangorestframework-gis, there exists the DistanceToPointFilter which returns results within a certain distance from a given point. How can I then have the results ordered by distance from that point, without repeating any distance calculations?
This is what my DRF view currently looks like:
class PlaceList(generics.ListAPIView):
queryset = Place.objects.filter(active=True)
serializer_class = ListSerializer
distance_filter_field = 'address.geometry'
filter_backends = (filters.DjangoFilterBackend, DistanceToPointFilter)
filter_class = PlaceFilter
ordering_fields = ('area', 'year_completed')

Sorting by column

I try to do something that can certainly be done easily but I can find a way.
I have a QtreeView displaying a QFileSystemModel that I custumized with two additionnal columns (sections). I work with directories named by date (YYYYMMDDHHMMSS+num). 'num' at the end of the string is a reference (an integer,ex: 04 or 12 or 53 ....) that can be similar to other directory name. 'num' is displayed in a fourth column.
I would like to group all directories with the similar references (for exemple in an ascending order) and also sort the directories by name (date) within each group.
Can you give me a hand please.
Folders looks like this:
201307
2013072400000053
2013072500000006
2013072600000053
2013072700000006
2013072800000006
2013072900000053
2013073000000006
2013073100000057
201308
2013082400000006
2013082500000053
2013082600000053
2013082700000057
2013082800000006
2013082900000057
2013083000000006
2013083100000053
...
code :
from PyQt4 import QtGui
from PyQt4 import QtCore
import sys
rootpathsource = " "
class MyFileSystemModel(QtGui.QFileSystemModel):
def columnCount(self, parent = QtCore.QModelIndex()):
# Add two additionnal columns for status and Instrument number
return super(MyFileSystemModel, self).columnCount() + 1
def headerData(self, section, orientation, role):
# Set up name for the two additionnal columns
if section == 4 and role == QtCore.Qt.DisplayRole :
return 'Number ref'
else:
return super(MyFileSystemModel, self).headerData(section, orientation, role)
def data(self, index, role):
if index.column() == 4: #if ref
ind = index.parent()
parentString = ind.data().toString()
if parentString in self.fileInfo(index).fileName() and self.fileInfo(index).isDir() == True and role == QtCore.Qt.DisplayRole:
return self.fileInfo(index).fileName()[-2:] # take the last two digits
else:
return super(MyFileSystemModel, self).data(index, role)
if role == QtCore.Qt.TextAlignmentRole:
return QtCore.Qt.AlignLeft
class TreeViewUi(QtGui.QWidget):
def __init__(self, parent=None):
super(TreeViewUi, self).__init__(parent)
self.model = MyFileSystemModel(self)
self.model.setRootPath(rootpathsource)
self.indexRoot = self.model.index(self.model.rootPath())
self.treeView = QtGui.QTreeView(self)
self.treeView.setExpandsOnDoubleClick(False)
self.treeView.setModel(self.model)
self.treeView.setRootIndex(self.indexRoot)
self.treeView.setColumnWidth(0,300)
self.layout = QtGui.QVBoxLayout(self)
self.layout.addWidget(self.treeView)
class MainGui(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MainGui,self).__init__(parent)
#QTreeView widget for files selection
self.view = TreeViewUi()
self.setCentralWidget(self.view)
self.resize(600,700)
def main():
main = MainGui()
main.show()
return main
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
StartApp = main()
sys.exit(app.exec_())
The way to do this is to use the QSortFilterProxyModel class.
All you have to do is create a subclass of it and reimplement its lessThan method. This method has two QModelIndex arguments which can be compared in any way you like before returning either True or False. In your case, you would first check which column was being sorted, and just return the value of the base-class lessThan method for the "standard" columns. But for the "Number Ref" column, you would first compare numerically, and then if that was equal, compare the values of the "Name" column for the same row.
To install the sort-filter proxy, you just need to do:
self.sortFilter = MyCustomSortFilterProxy(self)
self.sortFilter.setSourceModel(self.model)
self.treeView.setModel(self.sortFilter)

Resources