How can I create a MappingModel using pydantic? - pydantic

I am trying to use pydantic in order to create a JSON schema that would validate a generic mapping, something where I can have any key as string and each key can have any value assigned to it.
How can I do this?
So far I was successful in using BaseModel to create models for mappings where I know each key name and type, but in this case it should be any key as long is text (or follows a regex pattern for naming), without restrictions on the value assignment.

I'm not sure that I fully understand your case, but maybe a custom root type is what you need. Something like this:
from typing import Dict, Any
from pydantic import BaseModel
class Model(BaseModel):
__root__: Dict[str, Any]
def __iter__(self):
return iter(self.__root__)
def __getattr__(self, item):
return self.__root__[item]
m = Model.parse_obj({'key1': 'val1', 'key2': 'val2'})
assert m.key1 == "val1"

Related

Pydantic - Allow missing field

I have a pydantic model. One of its fields must be supplied by user, however, the second one can be present but it is totally okay if it is missing.
In case of missing age, I don't want it to be present on pydantic model instance at all.
My Model:
from pydantic import BaseModel
class Employee(BaseModel):
name: str
age: Optional[int]
Problem:
e = Employee(name="Harwey Smith")
e.age # Has age parameter equal to None, even if not provided.
How do I setup my Employee pydantic model not to have age attribute at all if it is not provided on init? If I would try to access e.nonexistent attribute, AttributeError would be thrown. This is what I want to happen for e.age, if not provided.
In my opinion, your case is rather unusual and I would always prefer to have a field with a None value. I think it is simpler and receiving an AttributeError is something unexpected if we consider the type hint that any IDE would show for age. Having said that, what you want is possible if you use a #rootvalidator to remove the age field:
from typing import Any, Dict, Optional
from pydantic import BaseModel, root_validator
class Employee(BaseModel):
name: str
age: Optional[int]
#root_validator(skip_on_failure=True)
def _remove_age_if_none(cls, values: Dict[str, Any]) -> Dict[str, Any]:
if "age" in values and values["age"] is None:
del values["age"]
return values
e = Employee(name="Harwey Smith")
print(e.age)
Output:
AttributeError: 'Employee' object has no attribute 'age'
This approach is something I haven't seen in the Pydantic documentation, but I have seen some answers that show that a #rootvalidator can be used to delete a field. In my opinion this behavior is odd and I would not be surprised if it changed in future releases of Pydantic. Be careful.

use request when using rest_framework.serializer

I use Django REST Framework and I have a django model class like this:
class Venue(models.Model):
user=models.ForeignKey(User)
I would then want to serialize this:
from rest_framework import serializers
import models
class VenueSerializer(serializers.ModelSerializer):
class Meta:
model=models.Venue
fields=('id','user','is_current_user')
where is_current_user is a boolean value, somehow like this
def is_current_user(self):
return self.request.user==self.user
How can I do this? Do I have request somewhere in serializer? Or should I do this somewhere in the model?
Less convenient options are:
to send the current user id to the client in another way and compare there, but then I'd have to expose the user of each model to the client.
to iterate over the json after serialization
manually without the serializers create a json from a queryset
I would suggest using SerializerMethodField:
class VenueSerializer(serializers.ModelSerializer):
class Meta:
model=models.Venue
fields=('id','user','is_current_user')
def get_is_current_user(self, obj):
return self.context['request'].user == obj.user

Django model - Foreign Key as Primary Key

I have the following 2 tables
In models.py
class Foo(models.Model):
uuid = models.CharField(_('UUID'), primary_key=True, default=uuid4)
and
class FooExt(models.Model):
uuid = models.ForeignKey(Foo, verbose_name=_('UUID'), primary_key=True)
time = models.DateTimeField(_('Create DateTime'), auto_now_add=True)
Basically, I have Foo and FooExt. I want a one-to-one relation between FooExt. That's why I set FooExt's primary key to be foreign key into Foo (not sure if this is the right thing to do).
Now I add an entry into Foo. Does an entry for FooExt automatically get created? Or do I need to manually add an entry to both Foo and FooExt?
Is there anything I can do to get the "automatic" add feature? Conceptually, these 2 tables describe the same thing, but I just don't want to pollute Foo with extra information. So it'd be great if an add to Foo automatically creates a corresponding FooExt.
If you want an OneToOne relation, then use models.OneToOneField instead of models.ForeignKey. with foreign keys you will need add unique=True in you ForeignKey:
class Foo(models.Model):
uuid = models.CharField(_('UUID'), primary_key=True, default=uuid4)
class FooExt(models.Model):
uuid = models.OneToOneField(Foo, verbose_name=_('UUID'), primary_key=True)
time = models.DateTimeField(_('Create DateTime'), auto_now_add=True)
No, an entry for FooExt don't get created when you create a Foo instance, you need to manually add an entry to both Foo and FooExt. think in Places and Restaurants, many places can be restaurants, but no all the places are restaurants.
if you like an automatic add feature inside Foo that create a FooExt instance, then you can overload the save method inside Foo that create and save FooExt instance too, something like this:
class Foo(models.Model):
....
....
def save(self, *args, **kwargs):
super(Foo, self).save(*args, **kwargs)
foo_ext = FooExt()
foo_ext.uuid = self
foo_ext.save()
Looks like there was mistake in Yonsy Solis answer in save method(corrected), try this:
class Foo(models.Model):
....
....
def save(self, *args, **kwargs):
super(Foo, self).save(*args, **kwargs)
foo_ext = FooExt()
foo_ext.uuid = self
foo_ext.save()
remark: i cant comment yet, so i decide to create answer
Have a look at the AutoOneToOneField in django-annoying
https://github.com/skorokithakis/django-annoying
or answer to this question: Can Django automatically create a related one-to-one model?

How can I create an alias name of a relational field in Odoo?

I need to create a model, to have backward compatibility with older field names.
This way,
I can develop modules that could read the "new" fields, but migrating the old ones is not necessary for this to work.
This works only for reading or presenting the fields, but not for writing them.
So I thought it would be good to create an alias for each field, and made this:
from openerp import models, fields, api
class backward_compatibility(models.Model):
_description = 'Backward compatibility'
_inherit = 'account.invoice'
new_document_class_id = fields.Integer(
compute='_comp_new_doc_class', string='Tipo')
new_document_number = fields.Char(
compute='_comp_new_doc_number', string='Folio')
#api.multi
def _comp_new_doc_class(self):
for record in self:
try:
record.new_document_class_id = record.old_document_class_id
except:
pass
#api.multi
def _comp_new_doc_number(self):
for record in self:
try:
record.new_document_number = record.old_document_number
except:
pass
This approach works for the Char field, but it doesn't for the Integer (Many2one).
What ideas do you have to make this work? Should I replicate the relationship in the new field?
oldname: the previous name of this field, so that ORM can rename it automatically at migration
Try to use "oldname". I saw this in the core modules. Never used personally.
_inherit = 'res.partner'
_columns = {
'barcode' : fields.char('Barcode', help="BarCode", oldname='ean13'),
}
Also dummy fields are user to help with backward compatibility.
'pricelist_id': fields.dummy(string='Pricelist', relation='product.pricelist', type='many2one'),

Scrapy: Default values for items & fields. What is the best implementation?

As far as I could find out from the documentation and various discussions on the net, the ability to add default values to fields in a scrapy item has been removed.
This doesn't work
category = Field(default='null')
So my question is: what is a good way to initialize fields with a default value?
I already tried to implement it as a item pipeline as suggested here, without any success.
https://groups.google.com/forum/?fromgroups=#!topic/scrapy-users/-v1p5W41VDQ
figured out what the problem was. the pipeline is working (code follows for other people's reference). my problem was, that I am appending values to a field. and I wanted the default method work on one of these listvalues... chose a different way and it works. I am now implementing it with a custom setDefault processor method.
class DefaultItemPipeline(object):
def process_item(self, item, spider):
item.setdefault('amz_VendorsShippingDurationFrom', 'default')
item.setdefault('amz_VendorsShippingDurationTo', 'default')
# ...
return item
Typically, a constructor is used to initialize fields.
class SomeItem(scrapy.Item):
id = scrapy.Field()
category = scrapy.Field()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self['category'] = 'null' # set default value
This may not be a clean solution, but it avoids unnecessary pipelines.