I have a successfully working inlineformset_factory form. I create this model with a button and inherit the data in it from another model. However, I was not able to access the inlineformset_factory fields in the other model.
def create_offer_button(request):
if request.method == "POST":
post = request.POST.get("post_pk")
obj = RequestModel.objects.get(pk=post)
if obj.is_accepted:
OfferModel.objects.create(request_model_name=obj,
request_title=obj.request_title,
delivery_time=obj.delivery_time,
shipping_country=obj.shipping_country,
shipping_address=obj.shipping_address,
preferred_currency=obj.preferred_currency,
shipping_term=obj.shipping_term,
delivery_term=obj.delivery_term)
obj.is_offer_created = True
obj.save()
return redirect(request.META.get("HTTP_REFERER"))
How can i access to RequestModel's inlineformset_factory fields?
I tried many methods but I could not reach the formset fields of the inheritance model.
I solved it:
obj_items = RequestItem.objects.filter(request_model=obj)
for item in obj_items:
OfferRequestItem.objects.create(offer_model_id=obj.id, product_name=item.product_name)
Related
I have started learning Django recently using a Udemy course. While going through the course instructor asked to save values from a Form to database.
After searching on the internet I figured out how to put form values into database and everything is working fine. Below is my views.py and forms.py files.
forms.py
class FormName(forms.Form):
fname = forms.CharField( label="First Name")
lname = forms.CharField(label="Last name:")
email = forms.EmailField()
verify_email = forms.EmailField(label='Verify Email:')
def clean(self):
all_clean_data = super().clean()
email = all_clean_data['email']
vmail = all_clean_data['verify_email']
if email != vmail:
raise forms.ValidationError("Check the emails")
views.py
def signup(request):
form = forms.FormName()
if request.method == 'POST':
form = forms.FormName(request.POST)
if form.is_valid():
post = User()
post.fname=request.POST.get('fname')
post.lname=request.POST.get('lname')
post.email=request.POST.get('email')
post.save()
return render(request,'third_app/greet.html')
else:
return render(request,'third_app/oops.html',{'form':form})
return render(request, 'third_app/signup.html',{'form':form})
Now coming to question, the instructor is using Meta class to store the form values to the database. Below are his forms.py and views.py files. I am curious about what the difference is between my method and the instructor's.
forms.py
class FormName(forms.ModelForm):
class Meta():
model = User
fields = 'all'
views.py
def signup(request):
form = forms.FormName()
if request.method == 'POST':
form = forms.FormName(request.POST)
if form.is_valid():
form.save(commit=True)
return render(request,'third_app/greet.html')
else:
return render(request,'third_app/oops.html',{'form':form})
return render(request, 'third_app/signup.html',{'form':form})
Thanks.
The Django docs explain this very well. It's what is known as a ModelForm:
If you’re building a database-driven app, chances are you’ll have forms that map closely to Django models. For instance, you might have a BlogComment model, and you want to create a form that lets people submit comments. In this case, it would be redundant to define the field types in your form, because you’ve already defined the fields in your model.
For this reason, Django provides a helper class that lets you create a Form class from a Django model.
So, to answer your question, your method uses a regular form (forms.Form) where you define the form fields, perform validation and then save each field individually in your view. When using form.ModelForm, field validation and saving is taken care of for you. Seeing as you have already defined what your fields are, the ModelForm uses this to perform the validation. The save() method conveniently saves each field to the database.
I'm creating a ModelForm:
# .. forms.py ..
class myform(forms.ModelForm):
img = forms.ImageField(required=True, label='Required photo')
class Meta:
model = mymodel
fields = '__all__'
# .. view.py ..
def myview(request):
..
if Request.method = 'POST':
form = forms.myform(request.POST, request.FILES)
..
The issue is that I don't want to do file operations in a view, but I would like to make all the magic right in the form save() method. I see the following ways to do it:
(1) somehow access ImageField instance and it's data
(2) somehow access request.FILES array via the form' instance
Please help me understand what way is the right one, and how could it be done?
I should have checked the sources from the very beginning. Each django form stores processed FILES array in a self.files variable.
I have a modelformset factory like so:
Form:
class AwesomeModelForm(ModelForm):
class Meta:
model = AwesomeModel
fields = ('thing', 'field_2', 'field_3')
widgets = {
'thing': HiddenInput(),
}
View:
class AwesomeView(TemplateView):
formset = modelformset_factory(AwesomeModel, form=AwesomeModelForm,
can_delete=True)
def get_context_data(self, **kwargs):
self.id = self.kwargs['id']
self.thing = Thing.objects.get(id=self.id)
formset = self.allocate_formset(queryset=AwesomeModel.objects\
.filter(thing=self.thing))
context = super(AllocateLeave, self).get_context_data(**kwargs)
context['formset'] = formset
return context
I'd like to prepopulate the modelformset_factory using initial e.g.
formset = self.allocate_formset(queryset=AwesomeModel.objects.filter(thing=self.thing), initial=[{'thing': self.thing,}])
Django seems to think that the application of "initial" means the form has changed, so if I want to change a form value but not the 'extra' form, the validation fails because the 'extra' form is incomplete.
e.g.
for form in new_formset.forms:
print form.has_changed()
returns True for the 'extra' field with the inital set and False if initial is not set in the formset declaration.
While iterating through the forms in the formset works to save each existing form, if I want to delete a form, or apply any formset wide validation, the form fails validation.
I think the solution might be to use a clean method for the 'thing' field, but I can't see how to access the context variables of the view in the modelform class. Is this the right approach or should I be pursuing another option?
Thanks
I am a little confused with where validation of form/model fields can happen in generic CreateView/UpdateView. Consider my hypothetical model below. I want the field secret to be hashed using my custom hashfunction and saved and assume some validation for secret field is done(NOT shown in the example below). My options to do this are:
1) in the model save method (I have not shown this below)
2) in the form's save method (I have shown below)
3) in the form_valid method of AccountCreateView (I have shown below)
4) how can I access the cleaned_data in the generic views (cleaned_data is available
only after form_valid is called)
Which is the right way to do it, both pros and cons. I will use the same form for updateView, in which case I will unhash the secret before displaying its data on the form. where this should happen?
My model:
class Account(models.Model):
user = models.ForeignKey(User)
created = models.DateField(auto_now=True)
secret = models.IntegerField()
My form:
AccountCreateForm(forms.ModelForm):
secret = forms.CharField(max_length=100)
class Meta:
model = MediaContent
exclude = (secret,user,created)
def save(self, user, debate):
obj = super(AccountCreateView, self).save(commit=False)
obj.user = self.cleaned_data['user']
obj.secret = myHashfunction(self.cleaned_data['secret'])
obj.save()
My view:
class AccountCreateView(CreateView):
"""
displays form to create a new search
"""
model = Account
form_class = AccountCreateForm
success_url = reverse_lazy('home')
template_name = 'app/account_form.html'
def form_valid(self, form):
f = form.save(commit=False)
secret=myHashfunction(self.request.POST['secret'])
f.user = self.request.user
f.save()
return super(AccountCreateView,self).form_valid(form)
EDIT:
please see the edit to my model and form. the field I use in form is not the field in model.
It is a new Field, that takes CharField but the model saves as IntegerField. my hashfunciton will convert the charField to IntegerField.
I think in this case Form is the better than ModelForm, as excluding every field on your model makes it redundant. You should then do any additional validation for the un-hashed secret here with clean_secret.
AccountCreateForm(forms.Form):
secret=forms.CharField(max_length=100)
Now, if you are not using the ModelForm anymore, I would suggest using FormView over CreateView as the generic CreateView has become less of a good fit.
class AccountCreateView(FormView):
"""
displays form to create a new search
"""
form_class = AccountCreateForm
success_url = reverse_lazy('home')
template_name = 'app/account_form.html'
def form_valid(self, form):
unhashed_secret = form.cleaned_data['secret']
hashed_secret = myHashfunction(unhashed_secret)
user = self.request.user
# Probably put some extra logic here to check if the user already exists
Account.objects.create(
user=user,
secret=hashed_secret,
)
return super(AccountCreateView,self).form_valid(form)
None of the above. The correct place to do this is in the clean_secret method of the form. That is the place for any field-related validation and conversion. Simply return the hashed value from that method.
I'm wondering if there's way in Django to associate a model to another, yet-to-be created model with a foreign key. Both model would be created using the same ModelForm in the same HTML page.
e.g.
class Team(models.Model):
name = forms.CharField
...
class Player(models.Model):
name = forms.CharField()
key = forms.ForeignKey(Team)
...
Basically, I'm wondering if both these models can be put in the same <form>...</form> in one HTML page.
a foreign key is a reference to the primary key of the referenced model, so the target needs to exist. you need to save the first form, and then update the reference on the second one before saving. to get a model instance from a form without saving to the db, you can use
instance = form.save(commit=False)
you then need to save the instance yourself
instance.save()
and if you are using many-to-many fields, you need to look at save_m2m
You may want to check the documentation for inlineformset, it allows to edit the related objects of a model in the same view, also see formsets.
def manage_teams(request, team_id):
team = Player.objects.get(pk=team_id)
PlayerInlineFormSet = inlineformset_factory(Player, Team)
if request.method == "POST":
formset = PlayerInlineFormSet(request.POST, request.FILES, instance=team)
if formset.is_valid():
formset.save()
# Do something.
else:
formset = PlayerInlineFormSet(instance=team)
return render_to_response("manage_teams.html", {
"formset": formset,
})
Here goes another example:
from django.forms.models import inlineformset_factory
def new_team(request):
PlayerInlineFormSet = inlineformset_factory(Team, Player)
team= Team()
if request.method == 'POST':
form_team = TeamForm(request.POST, request.FILES, instance= team, prefix= 'team')
form_player = PlayerInlineFormSet(request.POST, request.FILES, instance= team, prefix= 'players')
if form_team.is_valid() and form_player.is_valid():
form_team.save()
form_player.save()
return HttpResponseRedirect('/teams/%s/' % team.slug)
else:
form_team = TeamForm( instance= team, prefix= 'team')
form_player = PlayerInlineFormSet(instance= team, prefix= 'players')
return render_to_response('Teams/new_team.html', {'form_team': form_team, 'form_player':form_player}, context_instance=RequestContext(request))