I have two step of my WizardView process based ModelForm. I don't understand why, when I valide the second step django says me that a previous field are required. I try to send data between step like that:
forms.py
class RequestForm1(forms.ModelForm):
class Meta:
model = Product
fields = ('title', 'product_class', )
class RequestForm2(forms.ModelForm):
class Meta:
model = Product
fields = ( 'dimension_x', )
views.py
class RequestView(SessionWizardView):
instance = None
def get_template_names(self):
return [TEMPLATES[self.steps.current]]
def get_form_initial(self, step):
current_step = self.storage.current_step
if current_step == 'step2':
prev_data = self.storage.get_step_data('step1')
print(prev_data)
title = prev_data.get('step1-title', '')
product_class = prev_data.get('step1-product_class', '')
return self.initial_dict.get(step, {
'title': title,
'product_class': product_class
})
return self.initial_dict.get(step, {})
def done( self, form_list, **kwargs ):
self.instance.save()
return HttpResponseRedirect('/catalogue/request/')
Please find below my solution
forms.py
class RequestForm1(forms.ModelForm):
class Meta:
model = Product
fields = ('title', 'product_class', )
class RequestForm2(forms.ModelForm):
class Meta:
model = Product
fields = ( 'title', 'product_class', 'dimension_x', )
widgets = {
'title': forms.HiddenInput(),
'product_class': forms.HiddenInput()
}
views.py
class RequestView(SessionWizardView):
instance = None
def get_template_names(self):
return [TEMPLATES[self.steps.current]]
def get_form_instance( self, step ):
if self.instance is None:
self.instance = Product()
return self.instance
def get_form(self, step=None, data=None, files=None):
form = super(RequestView, self).get_form(step, data, files)
if step == 'step2':
prev_data = self.storage.get_step_data('step1')
title = prev_data.get('step1-title', '')
product_class = prev_data.get('step1-product_class', '')
form.fields['title'].initial = title
form.fields['product_class'].initial = product_class
return form
def done(self, form_list, **kwargs):
self.instance.save()
return HttpResponseRedirect('/catalogue/request/success')
Related
I want to pass a specific model from my view to my form's inner Meta class:
view:
#login_required
def product_create_view(request):
if request.method == 'POST':
create_product_form = CreateProductForm(request.POST, request=request, model=model)
if create_product_form.is_valid():
create_product_form.save()
else:
create_product_form = CreateProductForm(request=request, model=model)
return render(request, 'products/product_create.html', {'form': create_product_form})
form:
class CreateProductForm(ModelForm):
class Meta:
model = CreateProductForm.model
fields = (
'title',
'description',
'price',
'stock',
'category'
)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
self.model = kwargs.pop('model')
super().__init__(*args, **kwargs)
Is that possible?
This won't work, as the form with its fields is created when the file is loaded. You can probably move the class definition into the function and define it dynamically.
#login_required
def product_create_view(request):
class CreateProductForm(ModelForm):
class Meta:
model = model
fields = (
'title',
'description',
'price',
'stock',
'category'
)
if request.method == 'POST':
create_product_form = CreateProductForm(request.POST, request=request)
if create_product_form.is_valid():
create_product_form.save()
else:
create_product_form = CreateProductForm(request=request)
return render(request, 'products/product_create.html', {'form': create_product_form})
I haven't tried it though.
I have a ModelForm and in a ModelChoiceField I need to filter objects by request.user. When data is submitted, I got the error "init() got multiple values for argument 'crescator' ". How can I repair that?
#My Form
class AdaugaPereche(forms.ModelForm):
boxa = forms.IntegerField(label="Boxa", min_value=1)
sezon = forms.CharField(label="Sezon reproducere", initial=datetime.now().year)
mascul = forms.ModelChoiceField(queryset=None, label="Mascul", empty_label="Alege mascul")
femela = forms.ModelChoiceField(queryset=None, label="Femela", empty_label="Alege femela")
serie_pui_1 = forms.TextInput()
serie_pui_2 = forms.TextInput()
culoare_pui_1 = forms.ModelChoiceField(queryset=None, label="Culoare pui 1", empty_label="Alege culoarea",
required=False)
culoare_pui_2 = forms.ModelChoiceField(queryset=None, label="Culoare pui 2", empty_label="Alege culoarea",
required=False)
data_imperechere = forms.DateInput()
primul_ou = forms.DateInput()
data_ecloziune = forms.DateInput()
data_inelare = forms.DateInput()
comentarii = forms.TextInput()
# Functie pentru filtrarea rezultatelor dupa crescator
def __init__(self, crescator, *args, **kwargs):
super(AdaugaPereche, self).__init__(*args, **kwargs)
self.fields['mascul'].queryset = Porumbei.objects.filter(crescator=crescator, sex="Mascul",
perechi_masculi__isnull=True)
self.fields['femela'].queryset = Porumbei.objects.filter(crescator=crescator, sex="Femelă",
perechi_femele__isnull=True)
self.fields['culoare_pui_1'].queryset = CuloriPorumbei.objects.filter(crescator=crescator)
self.fields['culoare_pui_2'].queryset = CuloriPorumbei.objects.filter(crescator=crescator)
class Meta:
model = Perechi
fields = "__all__"
#My view
def perechenoua(request):
if request.method == "POST":
form = AdaugaPereche(request.POST, crescator=request.user)
if form.is_valid():
obj = form.save(commit=False)
obj.crescator = request.user
obj.save()
return HttpResponseRedirect("/perechi/")
else:
form = AdaugaPereche(crescator=request.user)
context = {
'form': form
}
template = loader.get_template("adauga-pereche.html")
return HttpResponse(template.render(context, request))
May the problem be obj = form.save(commit=False) obj.crescator = request.user obj.save() ?
The __init__ function of the form signature takes as first parameter here crescator, but you pass request.POST as that parameter, and furthermore you also pass this as a named parameter. By reordening the parameter, this should work:
class AdaugaPereche(forms.ModelForm):
# ...
def __init__(self, *args, crescator=None, **kwargs):
super(AdaugaPereche, self).__init__(*args, **kwargs)
self.fields['mascul'].queryset = Porumbei.objects.filter(crescator=crescator, sex="Mascul",
perechi_masculi__isnull=True)
self.fields['femela'].queryset = Porumbei.objects.filter(crescator=crescator, sex="Femelă",
perechi_femele__isnull=True)
self.fields['culoare_pui_1'].queryset = CuloriPorumbei.objects.filter(crescator=crescator)
self.fields['culoare_pui_2'].queryset = CuloriPorumbei.objects.filter(crescator=crescator)
# ...
I have below models.py and admin.py files in Django. I wanted to do 2 things
Merge specs field of Environment and ItemObject and store into specs of Environment which I managed to do because I was able to figure out where to place the logic. (class AddEnvironmentDetailsInlineForm(forms.ModelForm))
I need to do the same for Estimate. I need to fetch the Environment.specs and Estimate.specs, merge the two and save in Estimate.specs
The merge is like ItemObject -> Environment -> Estimate
The challenge is that I cannot figure out where to put that logic for Estimate and Environment. Do I need to create a ModelForm for Estimate to achieve a merge? I am not clear on the logic here for the files (I am still learning the concepts in Django). If anyone could make me understand, it would be great.
models.py
class Estimate(CommonModel):
...
class Environment(CommonModel):
estimate = models.ForeignKey(Estimate,related_name='environments')
logic = PythonCodeField(blank=True, null=True)
...
class Item(CommonModel):
...
class ItemTemplate(Item):
pass
class ItemObject(Item):
pass
class EnvironmentDetail(models.Model):
environment = models.ForeignKey(Environment)
item_name = models.CharField(max_length=200,blank=True)
qty = models.FloatField('Qty')
...
admin.py
class CommonAdmin(admin.ModelAdmin):
...
class EnvironmentInlineAdmin (admin.TabularInline):
model = Environment
...
formfield_overrides = {
JSONField:{ 'widget':JSONEditor },
}
...
def save_model(self, request, obj, *kwargs):
if request.user.is_superuser or request.user==obj.author:
obj.author.id=request.user.id
super(EnvironmentInlineAdmin,self).save_model(request, obj, *kwargs)
else:
raise ValidationError("author must be you")
class EstimateAdmin (CommonAdmin):
inlines = [ EnvironmentInlineAdmin ]
list_display = ('title', 'gp_code', 'otc_price','annual_price')
admin.site.register(Estimate,EstimateAdmin)
class EnvironmentDetailsInlineForm(forms.ModelForm):
class Meta:
model = EnvironmentDetail
fields = ['item_name','qty']
show_change_link = True
class AddEnvironmentDetailsInlineForm(forms.ModelForm):
class Meta:
model = EnvironmentDetail
fields = ['item_name','item', 'qty']
item = forms.ModelChoiceField(queryset=ItemTemplate.objects.all())
def clean(self):
instance = self.cleaned_data['item']
item_specs = self.cleaned_data['item'].specs
environment_specs = self.cleaned_data['environment'].specs
environment_specs.update(item_specs)
fields = [f.name for f in Item._meta.fields]
values = dict( [(x, getattr(instance, x)) for x in fields] )
new_instance = ItemObject(**values)
new_instance.save() #save new one
#instance.delete() # remove the old one
self.cleaned_data['item'] = new_instance
return self.cleaned_data # Return self.cleaned_data at the end of clean()
def save_model(self, request, obj, *kwargs):
if request.user.is_superuser or request.user==obj.author:
super(AddEnvironmentDetailsInlineForm,self).save_model(request, obj, *kwargs)
else:
raise ValidationError("author must be you")
class EnvironmentDetailsInlineAdmin (admin.TabularInline):
model = EnvironmentDetail
...
form = EnvironmentDetailsInlineForm
formfield_overrides = {
JSONField:{ 'widget':JSONEditor },
}
def change_link(self, obj):
return mark_safe('Edit Item' % \
reverse('admin:estimate_itemobject_change',
args=(obj.item.id,)))
def item_type(self, obj):
return obj.item.item_type
...
class AddEnvironmentDetailsInlineAdmin (admin.TabularInline):
model = EnvironmentDetail
fields = ('item_name','item', 'qty', )
form = AddEnvironmentDetailsInlineForm
def has_change_permission(self, request, obj=None):
return False
def item(self,obj):
return ItemTemplate
class EnvironmentAdmin (CommonAdmin):
model=Environment
save_as = True
inlines = [ EnvironmentDetailsInlineAdmin,AddEnvironmentDetailsInlineAdmin]
...
def get_model_perms(self, request):
"""
Return empty perms dict thus hiding the model from admin index.
"""
return {}
admin.site.register(Environment,EnvironmentAdmin)
I tried below but now this(dict_merge) needs to be saved to Estimate
#receiver(post_save, sender=Estimate)
def update_specs_estimate(sender, instance, **kwargs):
print("instance", instance.specs)
return instance.specs
#receiver(post_save, sender=Environment)
def take_specs_environment(sender, instance, **kwargs):
print("instance", instance.specs)
return instance.specs
def dict_merge(update_specs_estimate, take_specs_environment):
return (update_specs_estimate.update(take_specs_environment))
I have a ModelForm for a parent model and and an InlineForm for a foreign key related model. The Inline form data is read only and can only be updated via a file upload. I have this working on the intial creation and save, but if you replace the students by uploading a new file, while the data updates properly after the upload and save, the inline form doesn't reload properly right after this save and displays an error. However, if you navigate away and return everything looks good.
Models.py
class Room(models.Model):
room_id = models.AutoField(primary_key=True)
name = models.TextField(blank=False, verbose_name = "Room Name ")
code = models.TextField(blank=False)
file = models.TextField(blank=False)
class Meta(object):
managed = False
db_table = 'public\".\"room'
class Student(models.Model):
room = models.ForeignKey(Room, related_name="students, to_field="room_id", db_column="room_id")
student_id = models.TextField(blank=False, primary_key=False)
#property
def grade(self):
return util.get_grade(self.student_id)
class Meta(object):
managed = False
db_table = 'public\".\"student’
admin.py
class StudentsInline(admin.TabularInline):
model = Student
form = StudentInlineForm
formset = StudentFormSet
readonly_fields = ('student_id', 'grade')
extra = 0
def get_actions(self, request):
'''
Removes the delete action
'''
actions = super(StudentsInline, self).get_actions(request)
del actions['delete_selected']
return actions
def has_delete_permission(self, request, obj=None):
return False
def has_add_permission(self, request, obj=None):
return False
class RoomAdmin(admin.ModelAdmin):
def student_count(self, obj):
return obj.students.count()
student_count.short_description = " Total Enrolled "
form = RoomForm
list_display = [name', ‘code']
readonly_fields = ['student_count']
list_per_page = 25
inlines = [StudentsInline]
def get_actions(self, request):
actions = super(RoomAdmin, self).get_actions(request)
del actions['delete_selected']
return actions
def has_delete_permission(self, request, obj=None):
return False
def save_formset(self, request, form, formset, change):
form.instance.file = 'processed file'
form.instance.save
formset.save()
forms_admin.py
class RoomsForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(RoomsForm, self).__init__(*args, **kwargs)
self.fields[‘name'].required = True
self.fields[‘code'].required = True
self.fields['file'] = forms.FileField(
label='Upload your enrollees:'
def load_file(self, upload_file):
student_list = []
data = upload_file.read() #simplified for this example
for s in reader:
s_id = s[0]
new_student = Student(student_id=s_id)
student_list.append(new_student)
return student_list
def save(self, *args, **kwargs):
room_form = super(RoomsForm, self).save(commit=False)
enrollee_list = self.load_file(self.instance.file.file)
if instance.room_id is None:
instance.file = 'uploaded'
instance = super(RoomsForm, self).save(commit=True)
Student.objects.filter(room_id=self.instance.room_id).all().delete()
instance.students.add(*enrollee_list)
return instance
class Meta:
model = Room
fields = '__all__'
class StudentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(StudentForm, self).__init__(*args, **kwargs)
I am trying to incorporate inline add to my django form. The User can create a 'Site', within the 'Site' form the user can create multible 'Staff' to that 'Site'.
I have followed a tutorial which I believe to be the solution but can not get it to work.
Currently I am getting the error:
'Calling modelformset_factory without defining 'fields' or 'exclude' explicitly is prohibited.'
Here is my attempt.
models.py
class Site(models.Model):
...
class Staff(models.Model):
site = models.ForeignKey(Site)
....
views.py
class BaseNestedFormset(BaseInlineFormSet):
def add_fields(self, form, index):
# allow the super class to create the fields as usual
super(BaseNestedFormset, self).add_fields(form, index)
form.nested = self.nested_formset_class(
instance=form.instance,
data=form.data if self.is_bound else None,
prefix=' %s-%s' % (
form.prefix,
self.nested_formset_class.get_default_prefix(),
),
)
def is_valid(self):
result = super(BaseNestedFormset, self).is_valid()
if self.is_bound:
# look at any nested formsets, as well
for form in self.forms:
result = result and form.nested.is_valid()
return result
def save(self, commit=True):
result = super(BaseNestedFormset, self).save(commit=commit)
for form in self:
form.nested.save(commit=commit)
return result
def nested_formset_factory(site_model, staff_model):
parent_child = inlineformset_factory(
site_model,
staff_model,
formset=BaseNestedFormset,
)
parent_child.nested_formset_class = inlineformset_factory(
staff_model,
)
return parent_child
class SiteCreate(CreateView):
model = Site
form_class = SiteForm
queryset = Site.objects.all()
success_url = '/site/list'
def get_form_class(self):
return nested_formset_factory(
Site,
Staff,
)
forms.py
class SiteForm(forms.ModelForm):
class Meta:
model = Site
exclude = ('creation', 'last_modified')
def nested_formset_factory(site_model, staff_model):
parent_child = inlineformset_factory(
site_model,
staff_model,
formset=BaseNestedFormset,
fields = ('one', 'two', 'ect')
)