Django - modelform + model property - django

I am trying to solve one issue about saving data in db.
This is an example how I think of it:
class MyModel(models.Model):
id = models.AutoField(primary_key=True)
fieldX = models.SomeFieldType()
#property:
def foo(self):
return self._foo
#foo.setter
def foo(self, var):
self._foo=var
class MyModelForm(models.Modelform):
class Meta:
model = models.MyModel
fields = '__all__'
The thing is I have dict that I am passing to this form (so I am not using view or any provided interaction with user directly. In dict I have some fields and what I want to do is one field that is passed to use it as model property but I do not want it to be saved in db.
So I tried something like:
form = MyModelForm(data_dict)
if form.is_valid():
form.foo = data_dict['data_for_property_not_db']
form.save()
Form does not know that my model has this property.
So basiclly what I want is to write some parts of my data_dict normaly to form and db as always ->works fine
and then I want some data_info pass to that property and use it somewhere in save() as needed without saving it to db itself.
Can you help me with this?

Related

Model with more than one ForeignKey, ForeignKey can be more than one for one Model

So to make the above possible I have found out that I have to have ManytoMany Field that is not a problem.
That field is in the form as follows:
class Form(forms.ModelForm):
class Meta:
model = MyModel
fields = ['notes', 'scan']
widgets = {
'scan': forms.CheckboxSelectMultiple(),
}
In the view I have this then:
form = Form(request.POST)
if from.is_valid():
inst = from.save(commit=False)
inst.something = something
inst.save()
Now what do I do, to save the test or scan from the form?
I tried :
inst.test.add(form.cleaned_data['test'])
But that doesn't work for test or scan.
The Model looks like this:
class MyModel(models.Model):
id = models.AutoField(primary_key=True)
notes = models.TextField(default='')
scan = models.ManyToManyField(Scan)
....
Please help I wasn't able find anything in the Internet about this
Thanks!
The documentation of the Form's save method tells it all: If you have a ModelForm that contains the model's ManyToManyField like this:
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ['__all__'] # or fields = ['scans'] assuming scans is the M2M field in MyModel
Then you have two ways to save the relationships:
Directly, using form.save()
Calling save_m2m() is only required if you use save(commit=False). When you use a simple save() on a form, all data – including many-to-many data – is saved without the need for any additional method calls.
Or indirectly because you want to manipulate the instance before saving:
if form.is_valid():
instance = form.save(commit=False)
instance.some_field = some_value
instance.save()
form.save_m2m() # this saves the relationships

Django Rest Framework – Custom Hyperlink field in serializer

How can I add a custom hyperlink field in a serializer? I would like to have a hyperlink field in my serializer that has query params in it. Since there is no way to pass query params from HyperlinkedRelatedField or HyperlinkedIdentityField as far as I know, I've tried using a SerializerMethodField. However, this only serializes to a string, and is not a clickable URL when I visit the API through my browser. My code looks something like this:
class MySerializer(serializers.HyperlinkedModelSerializer):
custom_field = serializers.SerializerMethodField()
class Meta:
model = MyModel
fields = ('url', 'custom_field')
def get_custom_field(self, obj):
result = '{}?{}'.format(
reverse('my-view'),
urllib.urlencode({'param': 'foo'})
)
return result
Also, I am having trouble understanding the difference between a HyperlinkedRelatedField and a HyperlinkedIdentityField, so a brief explanation would be appreciated.
This should do the trick:
from rest_framework.reverse import reverse
class MySerializer(serializers.HyperlinkedModelSerializer):
custom_field = serializers.SerializerMethodField()
class Meta:
model = MyModel
fields = ('url', 'custom_field')
def get_custom_field(self, obj):
result = '{}?{}'.format(
reverse('my-view', args=[obj.id], request=self.context['request']),
'param=foo'
)
return result
The reverse function in rest_framework takes a view name (whatever view you'd like to link to), either an args list (the object id, in this case) or kwargs, and a request object (which can be accessed inside the serializer at self.context['request']). It can additionally take a format parameter and any extra parameters (as a dictionary) that you want to pass to it.
The reverse function then builds a nice, fully-formed URL for you. You can add query params to it by simply adding as many ?{}&{}&{} to your result variable and then filling in the series of query params beneath the 'param=foo' inside your format function with whatever other params you want.
The HyperlinkedIdentityField is used on the object itself that is being serialized. So a HyperlinkedIdentifyField is being used in place of your primary key field on MyModel because you are using a HyperlinkedModelSerializer which creates a HyperlinkedIdentityField for the pk of the object itself being serialized.
The HyperlinkedRelatedField is used to define hyperlinked relationships to RELATED objects. So if there were a MySecondModel with a foreign key relationship to MyModel and you wanted to have a hyperlink on your MyModel serializer to all the related MySecondModel objects you would use a HyperlinkedRelatedField like so (remember to add the new field to your fields attribute in Meta):
class MySerializer(serializers.HyperlinkedModelSerializer):
custom_field = serializers.SerializerMethodField()
mysecondmodels = serializers.HyperlinkedRelatedField(
many=True
read_only=True,
view_name='mysecondmodel-detail'
)
class Meta:
model = MyModel
fields = ('url', 'custom_field', 'mysecondmodels')
def get_custom_field(self, obj):
result = '{}?{}'.format(
reverse('my-view', args=[obj.id], request=self.context['request']),
'param=foo'
)
return result
If it were a OneToOneField rather than ForeignKey field on MySecondModel then you would set many=False.
Hope this helps!

Django form - Can I create an object without saving it?

I have an object (myModel) which i want to create according to a form(myModelForm), I also want that the object would be related to a requiered ForeignKey object (Group) which i want to decide by myself.(i.e, don't want it to be in the form.
So if I try to use form.save() i get an error. is there a way i can add Group ForeignKey (in view) before i use save()?
My code looks something like this:
class myModel(models.Model):
myGroup = ForeignKey(Group)
normal_field1 = TextField()
...
normal_field2 = TextField()
class myModelForm(ModelForm):
class Meta:
model = myModel
fields = [normal_field1,noraml_field2]
muchas gracias
Use commit=False to create the item without persisting it to the database.
if form.is_valid():
obj = form.save(commit=False)
obj.myGroup= whatever
obj.save()

Django model form and objects database id

I have a complex django object, which has properties of other class types. This gets like this:
class Order:
contractor - type Person
some other fields....
In my form I'd like to be able to either choose existing Person object from the dropdown or add a new one with a form. I've managed to create forms and appropriate workflow, but the problem is with saving the Order itself, I simply can't get the id of a saved Person instance. I'm doing sth like this:
def make_order(request):
if request.method == 'POST':
parameters = copy.copy(request.POST)
contractor_form = ContractorForm(parameters)
if contractor_form.is_valid():
contractor_form.save()
parameters['contractor'] = ???
form = OrderForm(parameters)
if form.is_valid():
form.save()
return HttpResponseRedirect('/orders/')
else:
form = OrderForm()
contractor_form = ContractorForm()
return render_to_response('orders/make_order.html', {'order_form' : form, 'contractor_form' : contractor_form})
So, if POST request reaches this method I first check if ContractorForm have been filled - I assume that if the form is valid, it is meant to be used. If yes, than I save it and would like to assign the saved object's database id to appropriate field for OrderForm to find it.
All my forms are ModelForms.
The questions are:
Are there better ways to do this? (choose from dropdown or add in place) - better or more pythonic ;-)
How can I get saved objects id when using ModelForms?
Edited
My ContractorForm is:
class ContractorForm(ModelForm):
class Meta:
model = Contractor
Nothing fancy.
save() should return the newly created instance.
if contractor_form.is_valid():
instance = contractor_form.save()
parameters['contractor'] = instance
where id would be instance.id, or even better instance.pk.
pk vs. id:
Regardless of whether you define a
primary key field yourself, or let
Django supply one for you, each model
will have a property called pk. It
behaves like a normal attribute on the
model, but is actually an alias for
whichever attribute is the primary key
field for the model. You can read and
set this value, just as you would for
any other attribute, and it will
update the correct field in the model.
Follow-up on comment:
Well it does work by default, so there must be something else wrong.
models.py
class Category(models.Model):
name = models.CharField(max_length=70)
slug = models.SlugField()
forms.py
from django import forms
from models import Category
class MyModelForm(forms.ModelForm):
class Meta:
model = Category
Test in shell:
In [3]: from katalog.forms import MyModelForm
In [4]: data = {'name':'Test', 'slug':'test'}
In [5]: form = MyModelForm(data)
In [6]: instance = form.save()
In [7]: instance
Out[7]: <Category: Test>
In [8]: instance.id
Out[8]: 5L

How to render a text box instead of a select box when using model form set

When I render my formset, one of the field renders as a select box because it is a foreign field in the model. Is there a way to change this to a text input? I want to populate that field by using Ajax auto complete. Adding a widget to the modelform is not working because the modelformset_factory takes a model and not a model form.
EDIT
My Model Form
class RecipeIngredientForm(ModelForm):
class Meta:
model = RecipeIngredient
widgets = { 'ingredient' : TextInput(), }
I use it in my view
RecipeIngredientFormSet = modelformset_factory(RecipeIngredient, form=RecipeIngredientForm)
objRecipeIngredients = RecipeIngredientFormSet()
EDITED MODEL FORM
class RecipeIngredientForm(ModelForm):
ingredient2 = TextInput()
class Meta:
model = RecipeIngredient
I create the form set like this
RecipeIngredientFormSet = modelformset_factory(RecipeIngredient, form=RecipeIngredientForm)
objRecipeIngredients = RecipeIngredientFormSet()
QUESTION
Do I have to use the formset in html? Can I just hard code the fields that get generated and using javascript I can create new fields and increment the "form-TOTAL-FORMS"? If I can then I do not have to worry about my model form.
Thanks
modelformset_factory does take a form. Here's the function signature from django.forms.models:
def modelformset_factory(
model, form=ModelForm, formfield_callback=lambda f: f.formfield(),
formset=BaseModelFormSet,
extra=1, can_delete=False, can_order=False,
max_num=0, fields=None, exclude=None):
If this isn't working for you, show some code and I'll try and see what is going wrong.
Edit after various comments As you point out, the widget argument is buggy when used in this way. So the solution is not to use it - it's a very recent addition in any case. Instead, define the field directly on the form:
class RecipeIngredientForm(forms.ModelForm):
ingredient = forms.ModelChoiceField(widget=forms.TextInput))
class Meta:
model = RecipeIngredient