suppose I have this model:
id = models.AutoField(primary_key=True)
datetime_from = models.DateTimeField('time')
model = MyModel
def read(self, request, id=0):
if not id:
id = 0
MyModel.object
mmodel = MyModel.objects.filter(id__gt=id)
return mmodel
My question is, how do I possible get the model fields individually, because I need to validate the datetime_from before I have to output the data on json format
You don't need to create an id column yourself. The read method is not necessary, either. Just say
from django.db import models
class MyModel(models.Model):
datetime_from = models.DateTimeField('time')
You can then query the table like so
>>> obj = MyModel.objects.get(id=<target id here>)
>>> date = obj.datetime_from
etc.
Related
In my model I need an ID field that is different from the default ID given by Django. I need my IDs in the following format: [year][ascending number]
Example: 2021001,2021002,2021003
The IDs shall not be editable but model entries shall take the year for the ID from a DateTimeField in the model. I am unsure if I use a normal Django ID and also create some kind of additional ID for the model or if I replace the normal Django ID with a Custom ID.
This problem is pretty similar to one I had solved for a previous project of mine. What I had done for this was to simply use the default id for the primary key, while using some extra fields to make the composite identifier needed.
To ensure the uniqueness and the restarting of the count I had made a model which would (only by the logic, no actual constraints) only have one row in it. Whenever a new instance of the model which needs this identifier would be created this row would be updated in a transaction and it's stored value would be used.
The implementation of it is as follows:
from django.db import models, transaction
import datetime
class TokenCounter(models.Model):
counter = models.IntegerField(default=0)
last_update = models.DateField(auto_now=True)
#classmethod
def get_new_token(cls):
with transaction.atomic():
token_counter = cls.objects.select_for_update().first()
if token_counter is None:
token_counter = cls.objects.create()
if token_counter.last_update.year != datetime.date.today().year:
token_counter.counter = 0
token_counter.counter += 1
token_counter.save()
return_value = token_counter.counter
return return_value
def save(self, *args, **kwargs):
if self.pk:
self.__class__.objects.exclude(pk=self.pk).delete()
super().save(*args, **kwargs)
Next suppose you need to use this in some other model:
class YourModel(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
yearly_token = models.IntegerField(default=TokenCounter.get_new_token)
#property
def token_number(self):
return '{}{}'.format(self.created_at.year, str(self.yearly_token).zfill(4))
#classmethod
def get_from_token(cls, token):
year = int(token[:4])
yearly_token = int(token[4:])
try:
obj = cls.objects.get(created_at__year=year, yearly_token=yearly_token)
except cls.DoesNotExist:
obj = None
return obj
Note: This might not be very refined as the code was written when I was very inexperienced, and there may be many areas where it can be refined. For example you can add a unique_for_year in the yearly_token field so:
yearly_token = models.IntegerField(default=TokenCounter.get_new_token, unique_for_year='created_at')
I have a model which contains sensitive data, let's say a social security number, I would like to transform that data on serialization to display only the last four digits.
I have the full social security number stored: 123-45-6789.
I want my serializer output to contain: ***-**-6789
My model:
class Employee (models.Model):
name = models.CharField(max_length=64,null=True,blank=True)
ssn = models.CharField(max_length=16,null=True,blank=True)
My serializer:
class EmployeeSerializer(serializers.ModelSerializer):
id = serializers.ReadOnlyField()
class Meta:
model = Employee
fields = ('id','ssn')
read_only_fields = ['id']
You can use SerializerMethodField:
class EmployeeSerializer(serializers.ModelSerializer):
id = serializers.ReadOnlyField()
ssn = SerializerMethodField()
class Meta:
model = Employee
fields = ('id','ssn')
read_only_fields = ['id']
def get_ssn(self, obj):
return '***-**-{}'.format(obj.ssn.split('-')[-1]
If you don't need to update the ssn, just shadow the field with a SerializerMethodField and define get_ssn(self, obj) on the serializer.
Otherwise, the most straightforward way is probably to just override .to_representation():
def to_representation(self, obj):
data = super(EmployeeSerializer, self).to_representation(obj)
data['ssn'] = self.mask_ssn(data['ssn'])
return data
Please add special case handling ('ssn' in data) as necessary.
Elaborating on #dhke’s answer, if you want to be able to reuse this logic to modify serialization across multiple serializers, you can write your own field and use that as a field in your serializer, such as:
from rest_framework import serializers
from rest_framework.fields import CharField
from utils import mask_ssn
class SsnField(CharField):
def to_representation(self, obj):
val = super().to_representation(obj)
return mask_ssn(val) if val else val
class EmployeeSerializer(serializers.ModelSerializer):
ssn = SsnField()
class Meta:
model = Employee
fields = ('id', 'ssn')
read_only_fields = ['id']
You can also extend other fields like rest_framework.fields.ImageField to customize how image URLs are serialized (which can be nice if you’re using an image CDN on top of your images that lets you apply transformations to the images).
In my Rest application I want to return json like JSONAPI format, but I need to create Schema class for it and create every field again that are already there in my model. So instead of creating every field in schema class can I not take it from DB Model..
below is my model class
class Author(db.Model):
id = db.Column(db.Integer)
name = db.Column(db.String(255))
I am defining Schema like below.
class AuthorSchema(Schema):
id = fields.Str(dump_only=True)
name = fields.Str()
metadata = fields.Meta()
class Meta:
type_ = 'people'
strict = True
So here, id and name I have defined it twice. so is there any option in marshmallow-jsonapi to assign model name in schema class so it can take all fields from model
Note: I am using marshmallow-jsonapifor it, I have tried marshmallow-sqlalchemy , it has that option but it not return json in JSONAPI format
You can use flask-marshmallow's ModelSchema and marshmallow-sqlalchemy in combination with marshmallow-jsonapi with the caveat that you have to subclass not only the Schema classes but also the SchemaOpts classes, like this:
# ...
from flask_marshmallow import Marshmallow
from marshmallow_jsonapi import Schema, SchemaOpts
from marshmallow_sqlalchemy import ModelSchemaOpts
# ...
ma = Marshmallow(app)
# ...
class JSONAPIModelSchemaOpts(ModelSchemaOpts, SchemaOpts):
pass
class AuthorSchema(ma.ModelSchema, Schema):
OPTIONS_CLASS = JSONAPIModelSchemaOpts
class Meta:
type_ = 'people'
strict = True
model = Author
# ...
foo = AuthorSchema()
bar = foo.dump(query_results).data # This will be in JSONAPI format including every field in the model
I have three models, one of which has an ID from each of the others to join them. The issue I am trying to solve is how I can get the values From table one into table three using table two to join them in the serializer, or if that is even the approach I should be taking to begin with.
model
class ModelOne(models.Model):
color = models.CharField()
size = models.CharField()
class ModelTwo(models.Model):
One_id = models.ForeignKey(ModelOne)
Three_id = models.CharField()
class ModelThree(models.Model):
example serializer
class ModelThree Serializer
model = ModelThree
fields = ('color', 'size')
def get_color(self, obj):
???
def get_size(self, obj):
???
my db structure
ModelOne
-------------------------------------
id color size
ModelTwo
-------------------------------------
id ModelOneID ModelThreeID
ModelThree
-------------------------------------
id
If I understand your question correctly, you have two models ModelOne and ModelThree which are joined by another model ModelTwo
class ModelOne(models.Model):
color = models.CharField(max_length=200)
size = models.CharField(max_length=200)
class ModelTwo(models.Model):
one = models.ForeignKey(ModelOne)
three = models.ForeignKey('ModelThree')
class ModelThree(models.Model):
some_field = models.CharField(max_length=200)
# this looks like a ManyToMany relationship between ModelThree and ModelOne
# where ModelTwo is the intermediate model
# you may want to check the docs https://docs.djangoproject.com/en/1.11/topics/db/models/#many-to-many-relationships
and you want to be able to query values from ModelOne into ModelThree.
If you have an instance of ModelThree m3, then you can get all related ModelOne instances by querying the other side of the foreignkey relation like so:
# ex: m3 = ModelThree.objects.first()
model_twos = m3.modeltwo_set.all()
for two in model_twos:
print two.one.color, two.one.size
I don't get it yet but you have: list id of Model 1 right?
~> You want to have model 3 (query by model2)
# The flow: models2.objects.filter(model1_id__in = md1_ids)
# And you want to get model3 ? ~> need ids to query
# ~> Query 1:
md3_ids = models2.objects.filter(model1_id__in = md1_ids).values_list("model3_id", flat="true")
md3 = Models3.objects.filter(pk__in md3_ids)
Is this it? I don't know what you want...
I would like to reflect on a model and list all its backward generic relations.
My model looks like this:
class Service(models.Model):
host = models.ForeignKey(Host)
statuses = generic.GenericRelation(Status)
The Status object looks like this:
class Status(TrackedModel):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey()
class Meta:
verbose_name_plural = 'statuses'
I would like to programatically learn that statuses is a generic relation for the Service model. Is this possible? Status._meta.fields does not show statuses, but Status._meta.get_all_field_names() does, only it shows other unwanted things too.
I thought that this might be a possible solution, but it seems really messy to me. I'd love to hear of a better one.
from django.db.models.fields import FieldDoesNotExist
from django.contrib.contenttypes import generic
generic_relations = []
for field_name in Service._meta.get_all_field_names():
try:
field = Service._meta.get_field(field_name)
except FieldDoesNotExist:
continue
if isinstance(field, generic.GenericRelation):
generic_relations.append(field)
Thank you!
The GenericRelation works similarly as ManyToManyField. You could find it in Service._meta.many_to_many:
filter(lambda f:isinstance(f, generic.GenericRelation), Service._meta.many_to_many)
UPDATE 2021:
To list all the GenericRelations() fields:
print(Service._meta.private_fields)
Output:
[<django.contrib.contenttypes.fields.GenericRelation: statuses>]
Nevertheless, if you have more fields with GenericRelations() relationship they will be shown into the output list.
Check the documentation:
https://docs.djangoproject.com/en/3.2/releases/1.10/#id3
Or you can return all of the fields that have a GenericRelation() field type.
ex:
# models.py
class MyModel(models.Model):
. . .
my_model_field = GenericRelation(OtherPolyModel)
def get_generic_relation_fields(self):
"""
This function returns all the GenericRelation
fields needed to return the values that are
related to a polymorphic model.
"""
fields = [f.attname for f in self.Meta.model._meta.get_fields()]
file_fields = []
for field in fields:
get_type = self.Meta.model._meta.get_field(field)
field_type = get_type.__class__.__name__
if field_type == "GenericRelation":
file_fields.append(field)
return file_fields
Output:
['my_model_field']