Multiple Validators for a single field - python

I have a table nsksystem which is getting value for its two field nskmachinename and nskreleaseid from another table nskrelease.
The requirement is nsksystem.nskreleaseid + nsksystem.nskmachinename should be unique and nsksystem.nskreleaseid is from nskrlease database. I am not enforcing any constraints in table.
db.define_table('nsksystem',
Field('nskuserid',length=512,requires=IS_EMAIL(error_message='invalid email!'),default = auth.user.email if auth.user else None, label=T('Email ID'),writable=False),
Field('nskmachinename', length=128, requires = IS_IN_DB(db,'nskrelease.nskname','%(nskname)s',error_message='Machine not registerd for release.'), label = T('Machine Name')),
Field('nskpassword', 'password', length=512,readable=False, label=T('Machine Password')),
Field('nskreleaseid',length=128, default='',label = T('Release'))
)
db.nsksystem.nskreleaseid.requires = [IS_IN_DB(db,'nskrelease.releaseid'), IS_NOT_IN_DB(db(db.nsksystem.nskmachinename == request.vars.nskmachinename), 'nsksystem.releaseid', error_message='Machine is already registered to the specified release.')]
In the above code the first require is enforced but I dont see a drop down for
IS_IN_DB(db,'nskrelease.releaseid')
And for the second require, when i try to give a conflicting input instead of giving me the desired error, a ticket is issued.

If you put the IS_IN_DB validator in a list, it will no longer generate the select widget. Instead of putting the validators in a list, you can use the _and argument:
db.nsksystem.nskreleaseid.requires = IS_IN_DB(db, 'nskrelease.releaseid',
_and=IS_NOT_IN_DB(db(db.nsksystem.nskmachinename == request.vars.nskmachinename),
'nsksystem.releaseid',
error_message='Machine is already registered to the specified release.'))

Related

How to restrict a Element with a name getting created to the Model that already have an element with the same name? Odoo 12

a = kw.get('a') #text getting from the user input
models = request.env['htpmodel']
for model in models:
if str(a) != str(model.name):
h = model.create({
'name': a,
})
If the str of user input(a) is not in the model's name needs to be created otherwise duplicate element needs not to be created
I would suggest searching for the record first using the name you are supplied:
a = kw.get("a")
models = request.env["htpmodel"]
# search to see if a record with that name already exists
record = models.search([("name", "=", a)], limit=1)
if not record:
# the record doesn't exist
h = model.create({
"name": a,
})
A query to the model would be better practice than to loop through the results.
Let me know if you need anything clarifying,
Thanks,

Django annotate model and filter for specific value

I'm trying to get into djangos annotate, but can't quite figure out how it works exactly.
I've got a function where I'd like to annotate a queryset of customers, filter them and return the number of customers
def my_func(self):
received_signatures = self.customer_set.annotate(Count('registrations').filter().count()
Now for the filter part, thats where I have a problem figuring out how to do that. The thing I'd like to filter for is my received_signatures, which is a function that is being called in my customer.py
def received_signatures(self):
signatures = [reg.brought_signature for reg in self.registrations.all() if reg.status == '1_YES']
if len(signatures):
return all(signatures)
else:
return None
brough_signature is a DB Field
So how can I annotate the queryset, filter for the received_signatures and then return a number?
Relevant Model Information:
class Customer(models.Model):
brought_signature = models.BooleanField(u'Brought Signature', default=False)
class Registration(models.Model):
brought_signature = models.BooleanField(u'Brought Signature', default=False)
status = models.CharField(u'Status', max_length=10, choices=STATUS_CHOICES, default='4_RECEIVED')
Note: A participant and a registration can have brought_signature. I have a setting in my program which allows me to either A) mark only brought_signature at my participant (which mean he brought the signature for ALL his registrations) or B) mark brought_signature for every registration he has
For this case Option B) is relevant. With my received_signatures I check if the customer has brought every signature for every registration where his status is "1_YES" and I want to count all the customers who did so and return a number (which I then use in another function for a pygal chart)
If I understand it correctly, you want to check if all the Registrations for a given Customer with status == '1_YES should have as attribute .brought_signature = True, and there should be at least such value. There are several approaches for this.
We can do this by writing it like:
received_signatures = self.customer_set.filter(
registration__status='1_YES'
).annotate(
minb=Min('registration__brought_signature')
).filter(
minb__gt=0
).count()
So what we here do is first .filter(..) on the registrations that have as status 1_YES, next we calculate for every customer a value minb that is the minimum of brought_signature of these Registrations. So in case one of the brought_signatures of the related Registrations is False (in a database that is usually 0), then Min(..) is False as well. In case all brought_signatures are True (in a database that is usually 1), then the result is 1, we can then filter on the fact that minb should thus be greater than 0.
So Customers with no Registration will not be counted, Customers with no Registration with status 1_YES, will not be counted, Customers with Registrations for which there is a Registration with status 1_YES, but with brough_signature will not be counted. Only Customers for which all Registrations that have status 1_YES (not per se all Registrations) have brough_signature = True are counted.

Associate classes with django-filters

Bonjour, I have a question regarding django-filters. My problem is:
I have two classes defined in my models.py that are:
class Volcano(models.Model):
vd_id = models.AutoField("ID, Volcano Identifier (Index)",
primary_key=True)
[...]
class VolcanoInformation(models.Model):
# Primary key
vd_inf_id = models.AutoField("ID, volcano information identifier (index)",
primary_key=True)
# Other attributes
vd_inf_numcal = models.IntegerField("Number of calderas")
[...]
# Foreign key(s)
vd_id = models.ForeignKey(Volcano, null=True, related_name='vd_inf_vd_id',
on_delete=models.CASCADE)
The two of them are linked throught the vd_id attribute.
I want to develop a search tool that allows the user to search a volcano by its number of calderas (vd_inf_numcal).
I am using django-filters and for now here's my filters.py:
from .models import *
import django_filters
class VolcanoFilter(django_filters.FilterSet):
vd_name = django_filters.ModelChoiceFilter(
queryset=Volcano.objects.values_list('vd_name', flat=True),
widget=forms.Select, label='Volcano name',
to_field_name='vd_name',
)
vd_inf_numcal = django_filters.ModelChoiceFilter(
queryset=VolcanoInformation.objects.values_list('vd_inf_numcal', flat=True),
widget=forms.Select, label='Number of calderas',
)
class Meta:
model = Volcano
fields = ['vd_name', 'vd_inf_numcal']
My views.py is:
def search(request):
feature_list = Volcano.objects.all()
feature_filter = VolcanoFilter(request.GET, queryset = feature_list)
return render(request, 'app/search_list.html', {'filter' : feature_filter, 'feature_type': feature_type})
In my application, a dropdown list of the possible number of calderas appears but the search returns no result which is normal because there is no relation between VolcanoInformation.vd_inf_numcal, VolcanoInformation.vd_id and Volcano.vd_id.
It even says "Select a valid choice. That choice is not one of the available choices."
My question is how could I make this link using django_filters ?
I guess I should write some method within the class but I have absolutely no idea on how to do it.
If anyone had the answer, I would be more than thankful !
In general, you need to answer two questions:
What field are we querying against & what query/lookup expressions need to be generated.
What kinds of values should we be filtering with.
These answers are essentially the left hand and right hand side of your .filter() call.
In this case, you're filtering across the reverse side of the Volcano-Volcano Information relationship (vd_inf_vd_id), against the number of calderas (vd_inf_numcal) for a Volcano. Additionally, you want an exact match.
For the values, you'll need a set of choices containing integers.
AllValuesFilter will look at the DB column and generate the choices from the column values. However, the downside is that the choices will not include any missing values, which look weird when rendered. You could either adapt this field, or use a plain ChoiceFilter, generating the values yourself.
def num_calderas_choices():
# Get the maximum number of calderas
max_count = VolcanoInformation.objects.aggregate(result=Max('vd_inf_numcal'))['result']
# Generate a list of two-tuples for the select dropdown, from 0 to max_count
# e.g, [(0, 0), (1, 1), (2, 2), ...]
return zip(range(max_count), range(max_count))
class VolcanoFilter(django_filters.FilterSet):
name = ...
num_calderas = django_filters.ChoiceFilter(
# related field traversal (note the connecting '__')
field_name='vd_inf_vd_id__vd_inf_numcal',
label='Number of calderas',
choices=num_calderas_choices
)
class Meta:
model = Volcano
fields = ['name', 'num_calderas']
Note that I haven't tested the above code myself, but it should be close enough to get you started.
Thanks a lot ! That's exactly what I was looking for ! I didn't understand how the .filter() works.
What I did, for other attributes is to generate the choices but in a different way. For instance if I just wanted to display a list of the available locations I would use:
# Location attribute
loc = VolcanoInformation.objects.values_list('vd_inf_loc', flat=True)
vd_inf_loc = django_filters.ChoiceFilter(
field_name='vd_inf_vd_id__vd_inf_loc',
label='Geographic location',
choices=zip(loc, loc),
)

How to prevent field value repetition on already assigned date in Odoo?

I am working with Odoo 10.
I have a one2many field with two columns in the hr.employee model. If the field "Bonus" (many2one field) is assigned to a particular date, it should not be saved or repeated once again on the same date.
How to achieve this?
Take a look at this below code, this is one possible solution, not the best.
from odoo import models, fields, api
from odoo.exceptions import ValidationError
class HrEmployee(models.Model):
_inherit = 'hr.employee'
prod_details_ids = fields.One2many(
string=u'Product details',
comodel_name='prod.details',
inverse_name='employee_id',
)
class ProdDetails(models.Model):
_name = 'prod.details'
employee_id = fields.Many2one(
string=u'Employee',
comodel_name='hr.employee',
)
date = fields.Date(
string=u'Date',
default=fields.Date.context_today,
)
bonus_id = fields.Many2one(
string=u'Bonus',
comodel_name='res.partner', # just an example
)
And then you need to add the constrains:
Solution 1
_sql_constraints = [
('bonus_unique', 'unique(employee_id, date, bonus_id)',
_('Date + Bonus cannot be repeated in one employee!')),
]
Solution 2
#api.one
#api.constrains('date', 'bonus_id')
def _check_unique_date(self):
# you have more freedom here if you want to check more things
rest = self.employee_id.prod_details_ids - self
for record in rest:
if record.date == self.date and record.bonus_id.id == self.bonus_id.id:
raise ValidationError("Date + Bonus already exists and violates unique field constraint")
Note: If you have date already in your database make sure that the constrains can be added with this data, because if not the constraint cannot be added to the database. This happens with the _sql_constraints at least
Use constrains to stop creating another record with the same name, so duplication of records doesnot occur.
you can use constraints and the search_count() method to check if there is a record. like below
#api.constraints('date')
def validate_date(self):
result = self.search_count([your_domain])
if result:
raise ValidationError(_('Your Text'))

How to update object with another object in get_or_create?

I have to tables wit similar fields and I want to copy objects from one table to another.
Problem that object could be absent in second table, so I have to use get_or_create() method:
#these are new products, they all are instances of NewProduct model, which is similar
#to Product model
new_products_list = [<NewProduct: EEEF0AP>, <NewProduct: XR3D-F>,<Product: XXID-F>]
#loop over them and check if they are already in database
for product in new_products_list:
product, created = Products.objects.get_or_create(article=product.article)
if created:
#here is no problem because new object saved
pass
else:
# here I need to code that will update existing Product instance
# with values from NewProduct instance fields
The case is that I don't want to list all fields for update manually, like this,, because I have about 30 of them:
update_old_product = Product(name=new_product.name,article= new_product.article)
Please advise more elegant way than above
You can loop over the field names and update them in the the other Product instance:
for new_product in new_products_list:
# use different variable names, otherwise you won't be able to access
# the item from new_product_list here
product, created = Products.objects.get_or_create(article=new_product.article)
if not created:
for field in new_product._meta.get_all_field_names():
setattr(product, field, getattr(new_product, field))
product.save()
You could try something like this.
def copy_fields(frm, to):
id = to.id
for field in frm.__class__._meta.fields:
setattr(to, field.verbose_name, field._get_val_from_obj(frm))
to.id = id
This is similar to Ashwini Chaudhary, although I think it will take care of that error that you mentioned in the comments.
new_products_list= (
# obj1, obj2, obj3 would be from [<NewProduct: EEEF0AP>, <NewProduct: XR3D-F>,<Product: XXID-F>] in your question
# NewProduct would just be the model that you import
# NewProduct._meta.fields would be all the fields
(obj1, NewProduct, NewProduct._meta.fields,),
(obj2, NewProduct, NewProduct._meta.fields,),
(obj3, NewProduct, NewProduct._meta.fields,),
)
for instance, model, fields in new_products_list:
new_fields = {}
obj, created = model.objects.get_or_create(pk=instance.article) # this is pretty much just here to ensure that it is created for filter later
for field in fields:
if field != model._meta.pk: # do not want to update the pk
new_fields[field.name] = request.POST[field.name]
model.objects.filter(pk=question_id).update(**new_fields) # you won't have to worry about updating multiple models in the db because there can only be one instance with this pk
I know this was over a month ago, but I figured I would share my solution even if you have already figured it out

Categories