Odoo - How to update a pre defined DATE field value - python-2.7

I'm trying to modify hr.contract model so the 'end_date' field gets the value of 'effective_date' which is in another model 'resignation_application'.
The concept is when an employee fill a resignation application it updates the contract end date.
Here's my code:
class resignation_application(osv.osv):
_name = 'resignation.application'
_columns = {
'employee_id': fields.many2one('hr.employee', "Employee", select=True, invisible=False, readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}),
'effective_date': fields.date('Effective Date', readonly=True, states={'draft':[('readonly',False)], 'confirm':[('readonly',False)]}, select=True, copy=False),
class hr_contract(osv.osv):
_inherit = 'hr.contract'
_columns = {
'end_date': fields.date('End Date', compute= '_compute_effective_date', store=True),
}
#api.model
def create(self, values):
if 'end_date' in values and not values['end_date']:
del(values['end_date'])
return super(hr_contract, self).create(values)
#api.one
#api.depends('end_date','employee_id')
def _compute_effective_date(self):
recs = self.env['resignation.application'] # retrieve an instance of MODEL
recs = recs.search([('state', '=', 'validate')]) # search returns a recordset
for rec in recs: # iterate over the records
if self.employee_id == rec.employee_id:
self.end_date = rec.effective_date
return recs.write({'end_date': rec.effective_date})
But it didn't return the end date.. I know there's something wrong with my return statement but I don't know how to fix it.. Also I want to add an inverse method to end_date field so the hr officer can add an end date to the employee contract. Any help will be appreciated.

First, you are mixing v8 (decorators) and v7 (osv.osv and _columns) stuffs.
Besides, in v8 you don't need to return anything nor write directly the end_date field in the compute method, but just set the field as you already did. Did you try to just remove the return statement?

Related

Django filter exact match for multi field: ManyToManyField using ModelMultipleChoiceFilter

I'm using Django filters (django-filter) in my project. I have the models below, where a composition (Work) has a many-to-many instrumentations field with a through model. Each instrumentation has several instruments within it.
models.py:
class Work(models.Model):
instrumentations = models.ManyToManyField(Instrument,
through='Instrumentation',
blank=True)
class Instrument(models.Model):
name = models.CharField(max_length=100)
class Instrumentation(models.Model):
players = models.IntegerField(validators=[MinValueValidator(1)])
work = models.ForeignKey(Work, on_delete=models.CASCADE)
instrument = models.ForeignKey(Instrument, on_delete=models.CASCADE)
views.py:
import django_filters
class WorkFilter(django_filters.FilterSet):
instrument = django_filters.ModelMultipleChoiceFilter(
field_name="instrumentation__instrument",
queryset=Instrument.objects.all())
My filter works fine: it grabs all the pieces where there is the instrument selected by the user in the filter form.
However, I'd like to add the possibility of filtering the compositions with those exact instruments. For instance, if a piece contains violin, horn and cello and nothing else, I'd like to get that, but not a piece written for violin, horn, cello, and percussion. Is it possible to achieve that?
I'd also like the user to choose, from the interface, whether to perform an exact search or not, but that's a secondary issue for now, I suppose.
Update: type_of_search using ChoiceFilter
I made some progress; with the code below, I can give the user a choice between the two kinds of search. Now, I need to find which query would grab only the compositions with that exact set of instruments.
class WorkFilter(django_filters.FilterSet):
# ...
CHOICES = {
('exact', 'exact'), ('not_exact', 'not_exact')
}
type_of_search = django_filters.ChoiceFilter(label="Exact match?", choices=CHOICES, method="filter_instruments")
def filter_instruments(self, queryset, name, value):
if value == 'exact':
return queryset.??
elif value == 'not_exact':
return queryset.??
I know that the query I want is something like:
Work.objects.filter(instrumentations__name='violin').filter(instrumentations__name='viola').filter(instrumentations__name='horn')
I just don't know how to 'translate' it into the django_filters language.
Update 2: 'exact' query using QuerySet.annotate
Thanks to this question, I think this is the query I'm looking for:
from django.db.models import Count
instrument_list = ['...'] # How do I grab them from the form?
instruments_query = Work.objects.annotate(count=Count('instrumentations__name')).filter(count=len(instrument_list))
for instrument in instrument_list:
instruments_query = instruments_query.filter(instrumentations__name=instrument_list)
I feel I'm close, I just don't know how to integrate this with django_filters.
Update 3: WorkFilter that returns empty if the search is exact
class WorkFilter(django_filters.FilterSet):
genre = django_filters.ModelChoiceFilter(
queryset=Genre.objects.all(),
label="Filter by genre")
instrument = django_filters.ModelMultipleChoiceFilter(
field_name="instrumentation__instrument",
queryset=Instrument.objects.all(),
label="Filter by instrument")
CHOICES = {
('exact', 'exact'), ('not_exact', 'not_exact')
}
type_of_search = django_filters.ChoiceFilter(label="Exact match?", choices=CHOICES, method="filter_instruments")
def filter_instruments(self, queryset, name, value):
instrument_list = self.data.getlist('instrumentation__instrument')
if value == 'exact':
queryset = queryset.annotate(count=Count('instrumentations__name')).filter(count=len(instrument_list))
for instrument in instrument_list:
queryset = queryset.filter(instrumentations__name=instrument)
elif value == 'not_exact':
pass # queryset = ...
return queryset
class Meta:
model = Work
fields = ['genre', 'title', 'instrument', 'instrumentation']
You can grab instrument_list with self.data.getlist('instrument').
This is how you would use instrument_list for the 'exact' query:
type_of_search = django_filters.ChoiceFilter(label="Exact match?", choices=CHOICES, method=lambda queryset, name, value: queryset)
instrument = django_filters.ModelMultipleChoiceFilter(
field_name="instrumentation__instrument",
queryset=Instrument.objects.all(),
label="Filter by instrument",
method="filter_instruments")
def filter_instruments(self, queryset, name, value):
if not value:
return queryset
instrument_list = self.data.getlist('instrument') # [v.pk for v in value]
type_of_search = self.data.get('type_of_search')
if type_of_search == 'exact':
queryset = queryset.annotate(count=Count('instrumentations')).filter(count=len(instrument_list))
for instrument in instrument_list:
queryset = queryset.filter(instrumentations__pk=instrument)
else:
queryset = queryset.filter(instrumentations__pk__in=instrument_list).distinct()
return queryset

A more efficient way to check that there isn't a related item that has a certain value

I have the following method in one of my model classes. It's designed to let my views know that an JournalEntry can be edited. It should return true if the entry is not more than 90 days old and does not have any related LineItems that have a value in the LineItems reconciled_date field. Ie all the related LineItems must have NULL in their reconciled_date field.
The method works but it iterates thru the LineItems which seems very inefficient. Is there a better way?
Models.py
class JournalEntry(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, null=False, blank=False)
date = models.DateField(null=False, blank=False)
def can_edit(self):
"""Checks if logged user can edit"""
is_reconciled = 0
for lineitem in self.lineitem_set.all():
if lineitem.reconciliation_date != None:
is_reconciled = 1
if (datetime.date.today() < (self.date + datetime.timedelta(90))) and is_reconciled == 0:
return True
else:
return False
Thank you
You can perform a single query that determines if the JournalEntry has any related line items that have a non-null reconciliation_date. It could be handy to have this in a property if you would like to re-use it in other methods
#property
def is_reconciled(self):
"""Returns True if any related line items have a non-null reconciliation_date"""
return self.lineitem_set.filter(reconciliation_date__isnull=False).exists()
Then you can use this in your method
def can_edit(self):
"""Checks if logged user can edit"""
# First check if the entry is less than 90 days old as it does not require
# a call to the DB
if self.date > datetime.date.today() - datetime.timedelta(days=90):
# If the entry is less than 90 days old then we return the inverse of is_reconciled
return not self.is_reconciled
return False

Filtering queryset if one value is greater than another value

I am trying to filter in view my queryset based on relation between 2 fields .
however always getting the error that my field is not defined .
My Model has several calculated columns and I want to get only the records where values of field A are greater than field B.
So this is my model
class Material(models.Model):
version = IntegerVersionField( )
code = models.CharField(max_length=30)
name = models.CharField(max_length=30)
min_quantity = models.DecimalField(max_digits=19, decimal_places=10)
max_quantity = models.DecimalField(max_digits=19, decimal_places=10)
is_active = models.BooleanField(default=True)
def _get_totalinventory(self):
from inventory.models import Inventory
return Inventory.objects.filter(warehouse_Bin__material_UOM__UOM__material=self.id, is_active = true ).aggregate(Sum('quantity'))
totalinventory = property(_get_totalinventory)
def _get_totalpo(self):
from purchase.models import POmaterial
return POmaterial.objects.filter(material=self.id, is_active = true).aggregate(Sum('quantity'))
totalpo = property(_get_totalpo)
def _get_totalso(self):
from sales.models import SOproduct
return SOproduct.objects.filter(product__material=self.id , is_active=true ).aggregate(Sum('quantity'))
totalso = property(_get_totalpo)
#property
def _get_total(self):
return (totalinventory + totalpo - totalso)
total = property(_get_total)
And this is line in my view where I try to get the conditional queryset
po_list = MaterialFilter(request.GET, queryset=Material.objects.filter( total__lte = min_quantity ))
But I am getting the error that min_quantity is not defined
What could be the problem ?
EDIT:
My problem got solved thank you #Moses Koledoye but in the same code I have different issue now
Cannot resolve keyword 'total' into field.Choices are: am, author, author_id, bom, bomversion, code, creation_time, description, id, inventory, is_active, is_production, itemgroup, itemgroup_id, keywords, materialuom, max_quantity, min_quantity, name, pomaterial, produce, product, slug, trigger_quantity, uom, updated_by, updated_by_id, valid_from, valid_to, version, warehousebin
Basically it doesn't show any of my calculated fields I have in my model.
Django cannot write a query which is conditioned on a field whose value is unknown. You need to use a F expression for this:
from django.db.models import F
queryset = Material.objects.filter(total__lte = F('min_quantity'))
And your FilterSet becomes:
po_list = MaterialFilter(request.GET, queryset = Material.objects.filter(total__lte=F('min_quantity')))
From the docs:
An F() object represents the value of a model field or annotated
column. It makes it possible to refer to model field values and
perform database operations using them without actually having to pull
them out of the database into Python memory

Django - Return JSON, Error

I got a View.py function that looks like this:
def GetAllCities(request):
cities = list(City.objects.all())
return HttpResponse(json.dumps(cities))
My City model looks like this
class City(models.Model):
city = models.CharField()
loc = models.CharField()
population = models.IntegerField()
state = models.CharField()
_id = models.CharField()
class MongoMeta:
db_table = "cities"
def __unicode__(self):
return self.city
I am using a MongoDB that looks like this
{
"_id" : ObjectId("5179837cbd7fe491c1f23227"),
"city" : "ACMAR",
"loc" : "[-86.51557, 33.584132]",
"state" : "AL",
"population" : 6055
}
I get the following error when trying to return the JSON from my GetAllCities function:
City ACMAR is not JSON serializable
So I tried this Instead:
def GetAllCities(request):
cities = serializers.serialize("json", City.objects.all())
return HttpResponse(cities)
And this works but It's very slow, it takes about 9 seconds(My database contains 30000 rows)
Should it take this long or am I doing something wrong?
I've built the same app in PHP, Rails and NodeJS.
In PHP it takes on average 2000ms, NodeJS = 800ms, Rails = 5882ms and Django 9395ms. Im trying to benchmark here so I wonder if there is a way to optimize my Django code or is this as fast as it gets?
For sure you do not need to return ALL cities, as you probably won't display all 30000 rows anyway (at least in user-friendly way). Consider a solution where you return only cities within some range from requested location. Mongo supports geospatial indexes, so there should be no problem in doing that. There are also many tutorials over the internet how to perform spatial filtering in Django/MongoDB.
def GetAllCities(request, lon, lat):
#Pseudo-code
cities = City.objects.filterWithingXkmFromLonLat(lon, lat).all()
cities = serializers.serialize("json", cities)
return HttpResponse(cities)
If you really, really need all cities, consider caching the response. Location, name and population of cities are not things which change dynamically, in a matter of let's say seconds. Cache the result and recalculate only every hour, day or more. Django supports cache out of the box
#cache_page(60 * 60)
def GetAllCities(request):
(...)
Another thing you can try to get a little more of speed is to get from db just the values you need and get the QuerySet to build the dictionary.
A simple query like this would work:
City.objects.all().values('id', 'city', 'loc', 'population', 'state')
Or you can put it in a manager:
class CitiesManager(models.Manager):
class as_dict(self):
return self.all().values('id', 'city', 'loc', 'population', 'state')
class City(models.Model):
.... your fields here...
objects = CitiesManager()
And then use it in your view as:
City.objects.as_dict()
FOUND A SOLUTION
I am benchmarking with different methods, one method is to see how fast one language/framework is to select ALL rows in a database and return it as JSON. I found a solution now that speeds it up by half the time!
My new views.py
def GetAllCities(request):
dictionaries = [ obj.as_dict() for obj in City.objects.all() ]
return HttpResponse(json.dumps({"Cities": dictionaries}), content_type='application/json')
And my new model
class City(models.Model):
city = models.CharField()
loc = models.CharField()
population = models.IntegerField()
state = models.CharField()
_id = models.CharField()
def as_dict(self):
return {
"id": self.id,
"city": self.city,
"loc": self.loc,
"population": self.population,
"state": self.state
# other stuff
}
class MongoMeta:
db_table = "cities"
def __unicode__(self):
return self.city
Found the solution here

How to add a filter in a custom manager?

I'm stuck with adding a filter to a custom manager in Django. This is my current custom manager that is working:
class VoteAwareManager(models.Manager):
def _get_score_annotation(self):
model_type = ContentType.objects.get_for_model(self.model)
table_name = self.model._meta.db_table
return self.extra(select={
'active': 'select active from %s mh where mh.main_id = %s.id and mh.active = true and mh.date_begin = (select max(date_begin) from euvoudebicicletaengine_mainhistoric where main_id = mh.main_id) and mh.date_end >= now()' % (MainHistoric._meta.db_table, table_name),
'row_num': '(row_number() over(order by (SELECT COALESCE(SUM(vote / ((extract(epoch from now() - time_stamp )/3600)+2)^1.5),0) FROM %s WHERE content_type_id=%d AND object_id=%s.id) DESC))' % (Vote._meta.db_table, int(model_type.id), table_name), # To know the position(#number) on the front page
'score': 'SELECT COALESCE(SUM(vote / ((extract(epoch from now() - time_stamp )/3600)+2)^1.5),0) FROM %s WHERE content_type_id=%d AND object_id=%s.id' % (Vote._meta.db_table, int(model_type.id), table_name)
}
)
def most_loved(self,):
return self._get_score_annotation().order_by('-score')
def most_hated(self):
return self._get_score_annotation().order_by('score')
I need to add a filter to the most_loved and most_hated to active=True that will be the SQL equivalent to where active=true in the main sql expression.
Any clues on how to do it?
I think you probably need to write a SQL view (to replace your extra() function) and create a new unmanaged model for the view (including active as a field in your model).
As in this question. Or this (possibly out of date) one.
Then use the view in your _get_score_annotation and add a filter to the queryset you're getting from that function.
def _get_score_annotation(self):
return ContentTypeView.objects.filter(# any filtering you need)
def most_loved(self,):
return self._get_score_annotation().filter(active=True).order_by('-score')