I'm using marshmallow for validate some fields of a schema.
class PlantDetailsSchema(Schema):
name: fields.Str(required=True, validate=validate.Length(min=3)),
sprout-time: fields.Str(required=True, validate=validate.Length(min=3)),
full-growth: fields.Str(required=True, validate=validate.Length(min=5)),
edible: fields.Bool(required=True)
class PlantInfoSchema(Schema):
plant = fields.Nested(PlantDetailsSchema)
num = fields.Int(required=True, validate=validate.Range(min=1))
def validate_json(json):
try:
schema = PlantInfoSchema().load(json)
ret = schema.dump()
return ret
except ValidationError:
return None
The json element to validate is:
json = {'plant': {'name': 'Bonsai', 'sprout-time': '3 months', 'full-growth': '2 years', 'edible': False}, 'num': 3}
My issue is how to validate fields that contains a dash in the middle of the name (like 'sprout-time' and 'full-growth'. Do you have some ideas to solve this issue?
You can use the 'data_key' attribute to specify the key name with dash. Define the field name something which is valid like 'sprout_time' (could be anything) and data_key as an attribute to the field with the actual name 'sprout-time'.
class PlantDetailsSchema(Schema):
name = fields.Str(required=True, validate=validate.Length(min=3))
sprout_time = fields.Str(
data_key='sprout-time',
required=True, validate=validate.Length(min=3)
)
full_growth = fields.Str(
data_key='full-growth',
required=True,
validate=validate.Length(min=5)
)
edible = fields.Bool(required=True)
Related
My objective is to create a new field based on another field after a Request is posted to FastAPI. My attempt was:
import functools
from datetime import date, datetime
from slugify import slugify
from sqlmodel import Field, SQLModel
class ProjectBase(SQLModel):
is_active: bool = Field(default=True, nullable=False)
name: str = Field(..., nullable=False)
publish_date: date = Field(default_factory=datetime.now().date, nullable=False)
# # post_init
repr_name: str = Field(
default_factory=functools.partial(slugify, name, separator='_'),
description="The project represented name, it must contain no whitespace, each word is separated by an underscore and it is slugified using the python-slugify library.",
nullable=False)
I have also tried __post_init__ but I think SQLModel does not have such a mechanism, it belongs to dataclasses within pydantic.
My desired output would be something like if a Request like the below was POST-ed:
request = {
'is_active': true,
'name': 'hello world bye',
'publish_date': '2023-01-01'
}
Then, the following Response is gotten and inserted into the database:
response = {
'is_active': true,
'name': 'hello world bye',
'repr_name': 'hello_world_bye', # output of slugify(`name`, separator='_')
'publish_date': '2023-01-01'
}
Thankfully sqlmodel.SQLModel inherits from pydantic.BaseModel. (And their metaclasses are also related.)
This is yet another job for custom validators. We just need a default sentinel value to check against, if no explicit value is provided by the user. I would just make the field type a union of str and None and make None the default, but then always ensure a str ends up as the value via the validator.
Here is an example:
from datetime import date, datetime
from typing import Any, Optional
from pydantic import validator
from slugify import slugify
from sqlmodel import Field, SQLModel
class ProjectBase(SQLModel):
name: str = Field(..., nullable=False)
publish_date: date = Field(default_factory=datetime.now().date, nullable=False)
repr_name: Optional[str] = Field(default=None, nullable=False)
#validator("repr_name", always=True)
def slugify_name(cls, v: Optional[str], values: dict[str, Any]) -> str:
if v is None:
return slugify(values["name"], separator="_")
return slugify(v, separator="_")
print(ProjectBase(name="foo bar baz").json(indent=2))
print(ProjectBase(name="spam", repr_name="Eggs and Bacon").json(indent=2))
Output:
{
"name": "foo bar baz",
"publish_date": "2023-02-11",
"repr_name": "foo_bar_baz"
}
{
"name": "spam",
"publish_date": "2023-02-11",
"repr_name": "eggs_and_bacon"
}
The always=True is important to trigger validation, when no value was explicitly provided.
The order of the fields is also important because the values dictionary in the validator method will only contain previously validated fields and field validation occurs in the order the fields were defined (see docs link above), so since name comes before repr_name, it will be validated and its value therefore present in the dictionary, when repr_name validation is triggered.
Important: Validation will not work, if the validators belong to a table=True model. To get around this, define a base model with the fields you need validated and inherit from that in your table=True model; then parse data through the parent model before instantiating the table model with it.
This bit seems pretty limiting, so I would not rule out the possibility that this will change in the future, at least to a certain extent. But in the current state of SQLModel that is what we have.
I am new to django and I am working on project where I am storing a key value pair dictionary as JSON in database.
Now later I want to show it on the html page all the list of packages but not able to access those key value pair as dictionary.
here is my
models.py
class Packages(models.Model):
package_ID = models.AutoField("Package ID", primary_key=True)
package_Name = models.CharField("Package Name", max_length=30, null=False)
attribute_values = models.CharField("Item Details JSON", max_length=500, null=False)
package_Price = models.IntegerField("Package Price", null=False, default=0.00)
quantity = models.IntegerField("Quantity", null=False, default=00)
prod_ID = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name="Product ID (FK)")
its data entry is something like this
package_ID = 1
package_Name = "basic card"
attribute_values = {"sizes": "8.5 in. x 11 in.", "Colour": "Full-Color Front - Unprinted Back",}
package_Price = 200
quantity = 400
prod_ID = 1
package_ID = 2
package_Name = "best card"
attribute_values = {"sizes": "8.5 in. x 11 in.", "Colour": "Full-Color Front - Unprinted Back",}
package_Price = 200
quantity = 500
prod_ID = 1
here the problem is that attribute_values fields stores a JSON string so I have to convert it in dictionary first and then have to access its dictionary.
the steps I want to do:
get all the packages having same product key:
now get the attribute_values of those packages
and convert that JSON into dictionary
then display the attribute_values of each package separately in html in particular block.
I want output something like this:
but I am getting this:
With Django version >=3.1 one can simply use JSONField for storing JSON in databases.
It is supported for the following databases:
JSONField is supported on MariaDB 10.2.7+, MySQL 5.7.8+, Oracle,
PostgreSQL, and SQLite 3.9.0+ (with the JSON1 extension
enabled).
Using the JSONField should be pretty simple:
class Packages(models.Model):
# other fields
attribute_values = models.JSONField()
Let's say we have an instance of Packages we can simply set a dictionary / variable that can be valid JSON on to attribute_values:
package.attribute_values = {"sizes": "8.5 in. x 11 in.", "Colour": "Full-Color Front - Unprinted Back",}
package.save()
This will be stored as JSON in the database.
When we want to access this in python it would automatically be deserialized to their python value:
package.attribute_values['new_key'] = 'new value'
package.save()
Considering your current problem with the text field you need to simply convert your string to equivalent python objects. This can be done using json.loads:
import json
attribute_values_dictionary = json.loads(package.attribute_values)
You can also add a method to your model to do this for you:
import json
class Packages(models.Model):
# Your fields
def attribute_values_to_python(self):
return json.loads(self.attribute_values)
Now in the view you can simply write:
attribute_values_dictionary = package.attribute_values_to_python()
In the template you can simply write:
{{ package.attribute_values_to_python }}
I am working on a project and i am required to store information entered into a form to a database column as json. The form does not have a model of its own but all its values will be stored as json into a column of another model. Here is the model:
class Document(models.Model):
user = models.ForeignKey(User)
document = models.JSONField(default = {})
category = models.CharField(max_length=255)
Now i am required to store json data from different forms (different categorys ) into the column document. Here is one category of such forms:
class InformalLetterForm(forms.Form):
sender_name = forms.CharField(max_length=45)
sender_address = forms.CharField(max_length=255)
date = forms.DateTimeField()
message_body = forms.CharField()
receiver_name = forms.CharField(max_length=255)
How do i serialize data entered in such a form to a json object to be stored in a database column (i.e the column document above).
i have searched online but i have seen serialization done only for data from model forms.
Thanks for any help..
You can call the .cleaned_data attribute from the Form, it will return a dictionary with the form's data in python, then you can call the .dumps() method from json python's library. Let's take an example from the docs:
>>> data = {'subject': 'hello',
... 'message': 'Hi there',
... 'sender': 'foo#example.com',
... 'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data
{'cc_myself': True, 'message': 'Hi there', 'sender': 'foo#example.com', 'subject': 'hello'}
Here you have a dictionary with your data, now let's make it a json:
import json
# An example simple dict
d = {'a': 1, 'b': 2}
json.dumps(d)
# '{"a": 1, "b": 2}'
I have a function which returns json data as history from Version of reversion.models.
from django.http import HttpResponse
from reversion.models import Version
from django.contrib.admin.models import LogEntry
import json
def history_list(request):
history_list = Version.objects.all().order_by('-revision__date_created')
data = []
for i in history_list:
data.append({
'date_time': str(i.revision.date_created),
'user': str(i.revision.user),
'object': i.object_repr,
'field': i.revision.comment.split(' ')[-1],
'new_value_field': str(i.field_dict),
'type': i.content_type.name,
'comment': i.revision.comment
})
data_ser = json.dumps(data)
return HttpResponse(data_ser, content_type="application/json")
When I run the above snippet I get the output json as
[{"type": "fruits", "field": "colour", "object": "anyobject", "user": "anyuser", "new_value_field": "{'price': $23, 'weight': 2kgs, 'colour': 'red'}", "comment": "Changed colour."}]
From the function above,
'comment': i.revision.comment
returns json as "comment": "changed colour" and colour is the field which I have written in the function to retrieve it from comment as
'field': i.revision.comment.split(' ')[-1]
But i assume getting fieldname and value from field_dict is a better approach
Problem: from the above json list I would like to filter new_field_value and old_value. In the new_filed_value only value of colour.
Getting the changed fields isn't as easy as checking the comment, as this can be overridden.
Django-reversion just takes care of storing each version, not comparing.
Your best option is to look at the django-reversion-compare module and its admin.py code.
The majority of the code in there is designed to produce a neat side-by-side HTML diff page, but the code should be able to be re-purposed to generate a list of changed fields per object (as there can be more than one changed field per version).
The code should* include a view independent way to get the changed fields at some point, but this should get you started:
from reversion_compare.admin import CompareObjects
from reversion.revisions import default_revision_manager
def changed_fields(obj, version1, version2):
"""
Create a generic html diff from the obj between version1 and version2:
A diff of every changes field values.
This method should be overwritten, to create a nice diff view
coordinated with the model.
"""
diff = []
# Create a list of all normal fields and append many-to-many fields
fields = [field for field in obj._meta.fields]
concrete_model = obj._meta.concrete_model
fields += concrete_model._meta.many_to_many
# This gathers the related reverse ForeignKey fields, so we can do ManyToOne compares
reverse_fields = []
# From: http://stackoverflow.com/questions/19512187/django-list-all-reverse-relations-of-a-model
changed_fields = []
for field_name in obj._meta.get_all_field_names():
f = getattr(
obj._meta.get_field_by_name(field_name)[0],
'field',
None
)
if isinstance(f, models.ForeignKey) and f not in fields:
reverse_fields.append(f.rel)
fields += reverse_fields
for field in fields:
try:
field_name = field.name
except:
# is a reverse FK field
field_name = field.field_name
is_reversed = field in reverse_fields
obj_compare = CompareObjects(field, field_name, obj, version1, version2, default_revision_manager, is_reversed)
if obj_compare.changed():
changed_fields.append(field)
return changed_fields
This can then be called like so:
changed_fields(MyModel,history_list_item1, history_list_item2)
Where history_list_item1 and history_list_item2 correspond to various actual Version items.
*: Said as a contributor, I'll get right on it.
Here is sample json reply needed for jquery ui autocomplete. Looks like only label and value are needed in my case.
I have the following code:
class City(db.Model):
'''Storage for cities ids.
Index
key_name: id of the city
parent: Country of the city
'''
city_name = db.StringProperty()
term = self.request.get('term')
query = City.all()
query.filter('city_name >=', term)
query.filter('city_name <=', unicode(term) + u"\ufffd")
cities = query.fetch(20, 0)
How to format result as json in the format like value = city_name, id = key_name?
I've also seen the following code somewhere, but it doesn't work for me:
map(lambda x: x.city_name(), cities)
You can use simplejson which is included in django.utils:
from django.utils import simplejson as json
Then create an array of dictionaries and json encode it:
city_array = []
for city in cities:
city_array.append({'value': city.city_name,
'label': city.city_name,
'id': city.key().name()})
json_message = json.dumps(city_array)