It's simple to require a field based on the state of another:
class FooForm(forms.Form)
foo = forms.BooleanField()
bar = forms.CharField(required=False)
def clean(self):
if self.cleaned_data.get('foo') and not self.cleaned_data.get('bar'):
raise forms.ValidationError('With foo you must have bar')
How can I do the reverse and remove a field requirement instead (without changing it in the model)?
E.g.
class FooForm(forms.Form)
foo = forms.BooleanField()
bar = forms.CharField(required=True)
def clean(self):
if not self.cleaned_data.get('foo'):
# No foo, no bar required
del bar.required??
Set bar to be not required, and in clean check if foo is False and bar doesn't exist:
class FooForm(forms.Form)
foo = forms.BooleanField()
bar = forms.CharField(required=False)
def clean(self):
super().clean()
# No foo and no bar, bad
if not self.cleaned_data.get('foo') and not self.cleaned_data.get('bar'):
raise forms.ValidationError("Bar is required without Foo")
Related
i need to join a table without a direct relationship.
models.py:
class FooModel():
bar = ForeignKey(Bar)
class BarModel():
pass
class BazModel():
bar = ForeignKey(Bar)
class QuxModel():
foo = ForeignKey(Foo)
tried to reach Foo from Baz but didn't work
viewset.py:
def BazView(viewsets.ModelViewSet):
queryset = model.BazModel.objects.all().prefetch_related('bar').prefetch_related('baz__bar')
serializer_class = serializer.Baz
def get_queryset(self):
return self.queryset
serializer.py
class FooSerializer(serializer.ModelSerializer):
class Meta:
model = FooModel
exlude = []
class BarSerializer(serializer.ModelSerializer):
class Meta:
model = BarModel
exlude = []
class BazSerializer(serializer.ModelSerializer):
foo = FooSerializer()
class Meta:
model = BarModel
exlude = []
class QuxSerializer(serializer.ModelSerializer):
class Meta:
model = QuxModel
exlude = []
using prefetch like that i got an error saying that Baz has no foo field.
also would like to get data from QuxModel based on Foo FK...
how could i perform this?
Try this:
model.BazModel.objects.all().select_related('bar').prefetch_related('bar__foomodel_set')
And then in BazSerializer, you can set the source for foo like this:
class BazSerializer(serializer.ModelSerializer):
foo = FooSerializer(source='bar.foomodel_set')
This will tell the foo serializer to get it's data from the FooModel objects using BazModel's bar.
You might have to change foomodel_set to the related name you have set to access FooModel from BarModel.
I POST to an API and relate the created Bar object to an existing Foo object. I use the name of Foo,
for example:
self.client.post('/app/bar/',
data={
'username': 'nikko123',
'foo': 'Nikko'
},
In my Bar Serializer, the Foo field has a ModelSerializer to relate it with,
class FooSerializer(serializers.ModelSerializer):
name = serializers.StringRelatedField()
link = serializers.SerializerMethodField()
class Meta:
model = Foo
fields = ('name', 'link')
def to_internal_value(self, data):
# Get name of Foo
foo = Foo.objects.get(name=data)
return {'foo': foo}
def get_link(self, object):
print('req', object.id)
if object.id and request:
return request.build_absolute_uri(
'/app/foo/{}'.format(object.id))
return None
class BarSerializer(serializers.ModelSerializer):
foo = FooSerializer(source='*')
...
class Meta:
model = Bar
My problem is that the response from Bar, I get empty from Foo.
{'id': 2,
'username': 'nikko123',
'foo': {'link': None}, # name is also missing
}
I have tested the Bar object and it saved the Foo, however it is not serializing.
What could be the problem here?
Say I have a model Foo that needs to refer to a value that can be one of several datatypes. To account for that I have several models for each datatype. Is it possible from within the Foo model to create a getValue method that would return the value from the first value method it can find connected with it?
example:
class Foo(models.Model):
def getValue(self):
if self.hasatr('foovalueint'):
return self.foovalueint
elif self.hasatr('foovaluestring'):
return self.foovaluestring
elif self.hasatr('foovaluedatetime'):
return self.foovaluedatetime
class FooValueInt(models.Model):
foo = models.OneToOneField(Foo, on_delete=CASCADE, primary_key=True)
value = models.IntegerField()
class FooValueString(models.Model):
foo = models.OneToOneField(Foo, on_delete=CASCADE, primary_key=True)
value = models.CharField(max_length=50)
class FooValueDateTime(models.Model):
foo = models.OneToOneField(Foo, on_delete=CASCADE, primary_key=True)
value = models.DateTimeField()
Or is there a better way to solve this multiple datatype issue?
I am trying to use google's ndb model, adding some auto fields and definitions prior to model definition. The below code works well. My question is, though, any specific ndb model implementation is not used ( given I will be destroyed, if google changes anything) do you see any issue with portability of below
class MetaModel(type):
def __new__(cls,name,bases,attrs):
super_new = super(MetaModel,cls).__new__
if name == "Model":
return super_new(cls,name,bases,attrs)
if attrs.get('auto_date_time',True):
attrs['date_add'] = ndb.DateTimeProperty(auto_now_add= True)
attrs['date_upd'] = ndb.DateTimeProperty(auto_now= True)
attrs['_get_kind'] = classmethod(get_kind)
attrs['__name__'] = name
attr_meta = attrs.get('Meta',None)
if attr_meta is None:
meta = type('meta',(object,),dict())
else:
meta = attr_meta
kwargs= {}
model_module = sys.modules[attrs['__module__']]
kwargs['app_label'] = model_module.__name__.split('.')[-2]
_meta = Options(meta,name,**kwargs)
attrs['_meta'] = _meta
return type(name,(ndb.Model,),attrs)
class Model(object):
__metaclass__ = MetaModel
class TesTModel(Model):
name = ndb.StringProperty(indexed=False)
tm = TestModel(name='This is the test model')
tm.put()
This seems pretty fragile. Sounds like an expando model might work for you?
Edit (based on clarification below): metaclasses and type really should be a last resort. They're confusing and hard to get right. For example, in your code snippet, subclasses of Model get ndb's metaclass instead of the one above:
class T1(Model):
pass
>>> T1().to_dict()
T1 {'date_upd': None, 'date_add': None}
class T2(Model):
auto_date_time = False
class T3(T2):
auto_date_time = True
>>> T2.__metaclass__
<class 'ndb.model.MetaModel'>
>>> T3().to_dict()
{}
You can avoid some craziness by deriving your metaclass from ndb.MetaModel:
class MyMetaModel(ndb.MetaModel):
def __new__(cls, name, bases, attrs):
return super(MyMetaModel,cls).__new__(cls, name, bases, attrs)
class MyModel(ndb.Model):
__metaclass__ = MyMetaModel
class AutoTimeModel(MyModel):
date_add = ndb.DateTimeProperty(auto_now_add=True)
date_upd = ndb.DateTimeProperty(auto_now=True)
Is it possible to do something like this?
class Doc1:
fieldd1 = StringField()
class Doc2:
fieldd2 = ReferenceField(Doc1.fieldd1)
Or should I just reference the Doc and then get the field information whenever I need it
This not posible and it is reference to document. To get fieldd1 you must do:
class Doc1(Document):
fieldd1 = StringField()
class Doc2(Document):
fieldd2 = ReferenceField(Doc1)
Doc2.objects.first().fieldd2.fieldd1
If you want just include document to another as part of one document then look at EmbeddedDocument and EmbeddedDcoumentField:
class Doc1(EmbeddedDocument):
fieldd1 = StringField()
class Doc2(Document):
fieldd2 = EmbeddedDcoumentField(Doc1)
Doc2.objects.first().fieldd2.fieldd1
But you always can set own properties:
class Doc1(Document):
fieldd1 = StringField()
class Doc2(Document):
fieldd2 = ReferenceField(Doc1)
#property
def fieldd1(self):
return self.fieldd2.fieldd1
Doc2.objects.first().fieldd1
See documentation: https://mongoengine-odm.readthedocs.org/en/latest/guide/defining-documents.html.