How to solve ValueError When save ModelMultipleChoiceField in ModelForm? - python

My model.py:
class RelayAddress(models.Model):
id = models.AutoField(primary_key=True,default=0)
sister_relay_relation = models.ManyToManyField('self', through='RelaySisterRelation',symmetrical=False)
def save(self, *args, **kwargs):
self.update_time = int(time.time())
super(RelayAddress,self).save(*args, **kwargs)
class RelaySisterRelation(models.Model):
id = models.AutoField(primary_key=True,default=0)
relay = models.ForeignKey(RelayAddress,related_name="relay")
sister_relay = models.ForeignKey(RelayAddress,related_name="sister_relay")
My admin.py
class RelaySisterRelationForm(forms.ModelForm):
relay=forms.ModelMultipleChoiceField(label=u'relay',widget=forms.CheckboxSelectMultiple(),queryset=RelayAddress.objects.all())
sister_relay=forms.ModelMultipleChoiceField(label=u'sister_relay',widget=forms.CheckboxSelectMultiple(),queryset=RelayAddress.objects.all())
def save(self, *args, **kwargs):
return super(RelaySisterRelationForm, self).save(*args,**kwargs)
And my view.py is null, then I get a ValueError:
Cannot assign "[<RelayAddress: RelayAddress object>]": "RelaySisterRelation.relay" must be a "RelayAddress" instance.
And how to solve this problem.

RelaySisterRelation.relay is a ForeignKey to RelayAddress meaning it can only store a references to one RelayAddress but your RelaySisterRelationForm.relay uses ModelMultipleChoiceField which is for many-to-many relations so returns a (potentially empty) list of RelayAddress instances.

Related

Django pass field from serializer to model.save() that is not present in the model

I need to pass fields that are present in serializer, but not present in model to model save method (I have complicated saving logic and I want to make some decisions in object creation based on these fields). How can I do that? I tried to add
non_db_field = property to model, but I still get error MyModel() got an unexpected keyword argument 'negative_amount'
Let's say my model is
class MyModel(AbstractModel):
field1 = models.DateTimeField()
field2 = models.BigIntegerField()
My serializer is
class MyModelSerializer(AbstractSerializer):
field3 = serializers.BooleanField(required=False)
class Meta(AbstractSerializer.Meta):
model = MyModel
fields = '__all__'
And my viewset is
class MyModelViewSet(AbstractViewSet):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer
You should handle this behavior in serializer.save method, for example, you can pop it from validated_data like that:
def save(self, **kwargs):
self.validated_data.pop("negative_amount")
return super().save(**kwargs)
You can use fields=['field1', 'field2', 'field3'] in serializer instead of fields='__all__'.
I found a solution based partly on Sharpek's answer and partly based on this answer:
In serializer I override save method:
def save(self, **kwargs):
if 'field3' in self.validated_data:
kwargs['field3'] = self.validated_data.pop('field3')
return super().save(**kwargs)
In models I override init method and define field:
field3 = None
def __init__(self, *args, **kwargs):
if 'field3' in kwargs:
self.field3 = kwargs.pop('field3')
super(Reading, self).__init__(*args, **kwargs)

how to get ManyToMany field data in model save method

class Product(models.Model):
varient_property = models.ManyToManyField(to='store.AttributeValue', blank=True)
def save(self, *args, **kwargs):
super(Product, self).save(*args, **kwargs)
# here varient_propery is manytomany field
# here i am saving this from django admin panel
print(self.varient_property.all())
many to many class
class AttributeValue(models.Model):
value = models.CharField(max_length=30)
available = models.BooleanField(default=True)
def __str__(self):
return self.value
the print statement returns none
how can I get many to many data after my model gets save?
I want all set many-to-many relations.
thanks in advance.
You can try this:
If you want all the records from AttributeValue model you need to use the all() method on the class not on the instance that you obtain from varient_property in Product model.
class Product(models.Model):
varient_property = models.ManyToManyField(to='store.AttributeValue', blank=True)
def save(self, *args, **kwargs):
super(Product, self).save(*args, **kwargs)
print(AttributeValue.objects.all()) #like this

Django save default value in Proxy Model

Different proxy models should be different in type.
If I query those models I the right ones.
I am trying to save a default type field in a proxy model.
I don't want to set it everytime in the view.
This does not work. The type field is always "TYPE1".
models.py:
class MyModel(models.Model):
class ModelType(models.TextChoices):
TYPE1 = 'TYPE1', _('TYPE1')
TYPE2 = 'TYPE2', _('TYPE2')
type = models.CharField(max_length=100, choices=ModelType.choices, default='TYPE1')
class Type2Manager(models.Manager):
def get_queryset(self):
return super(Type2Manager, self).get_queryset().filter(type='TYPE2')
def save(self, *args, **kwargs):
kwargs.update({'type': 'TYPE2'})
return super(Type2Manager, self).save(*args, **kwargs)
class Type2ProxyModel(MyModel):
class Meta:
proxy = True
objects = Type2Manager()
views.py:
def create_type2_model(request):
form = Type2Form(request.POST, initial={})
f = form.save(commit=False)
f.save()
forms.py:
class Type2Form(ModelForm):
class Meta:
model = Type2ProxyModel
Update 25.02.2020 12:18:
I found out that this sets the correct type. But I don't know how to use create() in a ModelForm.
class Type2Manager(models.Manager):
...
def create(self, **kwargs):
kwargs.update({'type': 'TYPE2'})
return super(Type2Manager, self).create(**kwargs)
Type2ProxyModel.objects.create()
A model manager operates on a "table-level". When you create an object via a form it uses the model objects and not the model manager and thus you'd need to override the save of your proxy model. If I modify your Type2ProxyModel to this it works:
class Type2ProxyModel(MyModel):
class Meta:
proxy = True
objects = Type2Manager()
def save(self, *args, **kwargs):
self.type = 'TYPE2'
return super(Type2ProxyModel, self).save(*args, **kwargs)

how to override django admin form Foreignkey based on request.user

admin.py
class PromoAdmin(admin.modelAdmin)
list_display = ( 'name', 'id', 'category', 'promo_type', 'store', 'brand', 'date_start' )
form = SampleForm
forms.py
class SampleForm(forms.ModelForm):
class Meta:
model = Promo
def __init__(self, request *args, **kwargs):
super(PromoAdminForm, self).__init__(*args, **kwargs)
self.fields["store"].queryset = Store.objects.filter(onwer=request.user)
got an error on request
Django Version: 1.3.1
Exception Type: TypeError
Exception Value:
init() takes at least 2 arguments (1 given)
You cannot initiate the store field with request.user in the field declaration. You can try the following:
class MyAwesomeForm(forms.ModelForm):
store = forms.ModelChoiceField(Store.objects)
class Meta:
model = Promo
def __init__(self, user, *args, **kwargs):
super(MyAwesomeForm, self).__init__(*args, **kwargs)
self.fields['store'].queryset = Store.objects.filter(owner=user)
While instantiating the form you can pass the request.user object.
myform = MyAwesomeForm(request.user)
If you want to achieve this in the admin you might try this
For providing only the objects related to the logged-in user in the admin provides the possibility to overwrite ModelAdmin.queryset function:
class MyModelAdmin(admin.ModelAdmin):
form = MyAwesomeAdminForm()
def queryset(self, request):
qs = super(MyModelAdmin, self).queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(store__owner=request.user)
class MyAwesomeAdminForm(forms.ModelForm):
class Meta:
model = Promo
Note that store__owner only works if you have a foreign key field stored in your promo model as such:
class Promo(models.Model):
store = models.ForeignKey(Store)
class Store(models.Model):
owner = models.ForeignKey(User)
I assume it should also be possible to somehow pass the request to the init method of the form. But did not find a suitable approach to do it.

Store form fields as key-values / individual rows

I have a simple form in Django that looks like this:
class SettingForm(forms.Form):
theme = forms.CharField(rrequired=True,
initial='multgi'
)
defaultinputmessage = forms.CharField(required=True,
initial='Type here to begin..'
)
...and the model to store it looks like:
class Setting(models.Model):
name = models.CharField(
null=False, max_length=255
)
value= models.CharField(
null=False, max_length=255
)
When the form is submitted, how can i store the form fields as key value pairs and then when the page is rendered, how can I initialize the form with the key's value. I've tried looking for an implementation of this but have been unable to find one.
Any help?
Thanks.
I'm assuming you want to store 'theme' as the name and the value as the value, same for defaultinputmessage. If that's the case, this should work:
form = SettingForm({'theme': 'sometheme', 'defaultinputmessage': 'hello'})
if form.is_valid():
for key in form.fields.keys():
setting = Setting.objects.create(name=key, value=form.cleaned_data[key])
Here's how I did it.
I needed to do this because I had a Model that stored information as key value pairs and I needed to build a ModelForm on that Model but the ModelForm should display the key-value pairs as fields i.e. pivot the rows to columns. By default, the get() method of the Model always returns a Model instance of itself and I needed to use a custom Model. Here's what my key-value pair model looked like:
class Setting(models.Model):
domain = models.ForeignKey(Domain)
name = models.CharField(null=False, max_length=255)
value = models.CharField(null=False, max_length=255)
objects = SettingManager()
I built a custom manager on this to override the get() method:
class SettingManager(models.Manager):
def get(self, *args, **kwargs):
from modules.customer.proxies import *
from modules.customer.models import *
object = type('DomainSettings', (SettingProxy,), {'__module__' : 'modules.customer'})()
for pair in self.filter(*args, **kwargs): setattr(object, pair.name, pair.value)
setattr(object, 'domain', Domain.objects.get(id=int(kwargs['domain__exact'])))
return object
This Manager would instantiate an instance of this abstract model. (Abstract models don't have tables so Django doesn't throw up errors)
class SettingProxy(models.Model):
domain = models.ForeignKey(Domain, null=False, verbose_name="Domain")
theme = models.CharField(null=False, default='mytheme', max_length=16)
message = models.CharField(null=False, default='Waddup', max_length=64)
class Meta:
abstract = True
def __init__(self, *args, **kwargs):
super(SettingProxy, self).__init__(*args, **kwargs)
for field in self._meta.fields:
if isinstance(field, models.AutoField):
del field
def save(self, *args, **kwargs):
with transaction.commit_on_success():
Setting.objects.filter(domain=self.domain).delete()
for field in self._meta.fields:
if isinstance(field, models.ForeignKey) or isinstance(field, models.AutoField):
continue
else:
print field.name + ': ' + field.value_to_string(self)
Setting.objects.create(domain=self.domain,
name=field.name, value=field.value_to_string(self)
)
This proxy has all the fields that I'd like display in my ModelFom and store as key-value pairs in my model. Now if I ever needed to add more fields, I could simply modify this abstract model and not have to edit the actual model itself. Now that I have a model, I can simply build a ModelForm on it like so:
class SettingsForm(forms.ModelForm):
class Meta:
model = SettingProxy
exclude = ('domain',)
def save(self, domain, *args, **kwargs):
print self.cleaned_data
commit = kwargs.get('commit', True)
kwargs['commit'] = False
setting = super(SettingsForm, self).save(*args, **kwargs)
setting.domain = domain
if commit:
setting.save()
return setting
I hope this helps. It required a lot of digging through the API docs to figure this out.

Categories

Resources