I would like to combine 2 querysets from 2 differents models, then I need to order them by date and finally my goal is to serialize it.
So far I did that :
last_actions = serializers.SerializerMethodField()
def get_last_actions(self, obj):
prc = obj.product_request_configs.all().order_by('modified_date')[:5]
psc = obj.product_send_configs.all().order_by('modified_date')[:5]
result_list = sorted(
chain(prc, psc),
key=attrgetter('modified_date'),
reverse=True)
But I don't know how to call my two django rest serializers so that I can return the right data.
If I could make a database view it coult be simpler I think.
Serializers are designed for match one model relationship, so we need to create a custom Model for the logic you are trying to achieve:
class CustomModel(models.Model):
def dictfetchall(self, cursor):
"""Returns all rows from a cursor as a dict"""
desc = cursor.description
return [dict(zip([col[0] for col in desc], row))
for row in cursor.fetchall()]
def yourMethod(self):
cursor = connection.cursor()
cursor.execute("""
select field1, field2 from app_table
where field1=%s and field2=%s group by field1
""",
[value1, value2,]
)
return self.dictfetchall(cursor)
class Meta:
abstract = True
This will return a dictionary and then you can serialize that response with a seializer like:
class CustomModelSerializer(serializers.Serializer):
field1 = serializers.IntegerField()
field2 = serializers.CharField()
Please note that on SQL you can use as keyword to rename some fields, the current name of fields must match var names in your serializer.
Related
I have a declarative class that has an Enum column, and the Enum has a property that returns True/False based on the specific enumerated name or value. It would simplify life if I could do a query with a filter based on this property, such as the following (see implementation below):
session.query(MyTable).filter(MyTable.letter.is_vowel)
using something like the below straightforward attempt at an expression fails with
AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with MyTable.letter has an attribute 'is_vowel'
The below implementation is too simple to allow for construction of the necessary query. Is there a way to do this? I thought maybe something in a Comparator might work, or maybe there's something more sophisticated that would do it?
import enum
from sqlalchemy import (
Column,
Enum,
Integer,
)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.hybrid import hybrid_property
MyDeclarativeBase = declarative_base()
class Letter(enum.Enum):
A = 1
B = 2
C = 3
D = 4
E = 5
# and so on...
#property
def is_vowel(self):
return self.name in 'AEIOU'
class MyTable(MyDeclarativeBase):
__tablename__ = 'my_table'
id = Column(Integer, primary_key=True, autoincrement=True)
letter = Column(Enum(Letter), nullable=False)
#hybrid_property
def is_vowel(self):
"""Return True if the row's letter is a vowel."""
return self.letter.is_vowel
#is_vowel.expression
def is_vowel(cls):
return cls.letter.is_vowel
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)
I have a following question:
for example I have 3 almost similar models:
Model_A(models.Model)
field_1 = ….
field_2 = ….
field_3 = ….
Model_B(models.Model)
field_1 = ….
field_2 = ….
field_3 = ….
Model_C(models.Model)
field_1 = ….
field_2 = ….
# no field 3 here
And I want to make a method in serializers.py where I would do something like that:
def funk(self, model)
# model is Model_A, B or C
model.objects.annotate(
result= Coalesce( F(“field_3), Value(value=1,output_field=IntegerField())
).etc_etc_etc…
return something here
Goal is like this:
if field_3 in model does exist, I want to get it’s value, if not, I want to get default value from Value().
Problem is that if field_3 in model doesn't exists it would through exception instead of just providing NULL value, which is logically correct...
Is it any way to implement such logic on a DB level by DB or ORM instruments?
Thank you
The Coalesce does not deal with a missing column, it deals with a value that is not there.
You can however simply inspect the model by accessing the _meta options, and see if there is such column, for example with .get_field(..) [Django-doc]:
from django.core.exceptions import FieldDoesNotExist
from django.db.models import Value, IntegerField
def funk(self, model):
try:
model._meta.get_field('field_3')
except FieldDoesNotExist:
return model.objects.annotate(
field_3=Value(1, output_field=IntegerField())
)
else:
return model.objects.all()
You can "parameterize" the column name, and run it for example several times to inject values for all missing columns in the model.
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')
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' )