Django ModeForm: no fields error - django

I have used the code in django 1.6 but when trying it in django 1.8.6 I get:
AttributeError: 'SignupDataForm' object has no attribute 'fields'
at this line, but also generaly whenever asking for fields:
merged_field_order = list(self.fields.keys())
My SignupDataForm is a child declared like this:
class SignupDataForm(BaseUserDataForm):
reg_user_badge = forms.CharField(label="Give Points to a Friend",required=False,widget = TextInput(attrs={'placeholder': 'Username (optional)'}),validators=[validate_friendname])
class Meta(BaseUserDataForm.Meta):
model = UserData
fields = BaseUserDataForm.Meta.fields + ('terms_conditions',)
#terms_conditions is also a model field but not added to the parent definition
def __init__(self, *args, **kwargs):
super(SignupDataForm, self).__init__(*args, **kwargs)
self.fields['terms_conditions'].required = False
self.fields['gender'].widget = Select(choices=GENDER_CHOICES,attrs={'class':'signup_select',})
self.fields['password2'].widget.attrs['onblur'] ="check_pass()"
self.fields['password1'].widget.attrs['onblur'] ="check_pass()"
def clean(self):
#clean overwrite
What is weird is that if I use the parent form everything works fine, I get no error. Also if I place a print fields in the META declaration the tuple with fields is there. The shortend code for the parent is here:
class BaseUserDataForm(forms.ModelForm):
url = forms.CharField(max_length = 30, label="Don't type here (anti spam protection)",validators=[validate_name_honeypots])
class Meta:
model = UserData
fields = ('****model fields named without terms_conditions field****')
def __init__(self, *args, **kwargs):
super(BaseUserDataForm, self).__init__(*args, **kwargs)
The error occurs in another child:
class BaseSignupForm(SignupDataForm):
username = forms.CharField(label=_("Username"),
max_length=get_username_max_length(),
min_length=app_settings.USERNAME_MIN_LENGTH,
widget=forms.TextInput(
attrs={'placeholder':
_('Username'),
'autofocus': 'autofocus'}))
email = forms.EmailField(widget=forms.TextInput(
attrs={'type': 'email',
'placeholder': _('E-mail address')}))
def __init__(self, *args, **kwargs):
email_required = kwargs.pop('email_required',
app_settings.EMAIL_REQUIRED)
self.username_required = kwargs.pop('username_required',
app_settings.USERNAME_REQUIRED)
super(BaseSignupForm, self).__init__(*args, **kwargs)
# field order may contain additional fields from our base class,
# so take proper care when reordering...
field_order = ['email', 'username']
merged_field_order = list(self.fields.keys())
Does anybody know what am I doing wrong?
UPDATE - SOLVED:
Solved. Not an python ModelForm or allauth issue at all. There was a form below, partially wrapped and partially commented that I didn't see and it caused an intendention error that manifest it such a weird manner ....

Related

Django admin does not show extra fields added in init method of modelform

I found a couple of questions regarding this, but I specifically wonder about how to add a field in the ModelForms __init__() method.
This is, because I get the number of fields from a function and need to display them in the admin:
class SomeForm(forms.ModelForm):
class Meta:
model = Product
fields = ["name", "price",]
def __init__(self, *args, **kwargs):
number_of_fields = get_number of fields(kwargs["instance"])
print(number_of_fields) ## e.g. 3, gives output
super().__init__(*args, **kwargs)
for i in range(number_of_fields):
self.fields[i] = forms.CharField("test", required = False)
But the fields do not show up in the Template Admin edit page. What did I miss? No error popping up either ...
Try something like this... but you need to pass field name into self.base_fields['name_of_the_field'] somehow
class SomeForm(forms.ModelForm):
class Meta:
model = Product
fields = ["name", "price",]
def __init__(self, *args, **kwargs):
number_of_fields = get_number_of_fields(kwargs["instance"])
print(number_of_fields) ## e.g. 3, gives output
for i in range(number_of_fields):
self.base_fields['name_of_the_field'] = forms.CharField(initial="test", required = False)
super(SomeForm, self).__init__(*args, **kwargs)

Django admin dynamic form fields population

I have Game model with ManyToMany relation on Taxonomy model witch has multiple types (PLATFORM, GENRE, FEATURE etc.)
class Game(models.Model):
taxonomy = models.ManyToManyField(Taxonomy)
class Taxonomy(models.Model):
TAXONOMY_ORDER = [
'PLATFORM',
'GAME_PROCESS',
'GRAPHICS',
'GENRE',
'CATEGORY',
'FEATURE'
]
type = models.CharField(choices=TAXONOMY_TYPES.items(), max_length=15)
I want to remove taxonomy field from admin and add separate MultiplueChoises field for each taxonomy type from TAXONOMY_ORDER
class GameAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(GameAdminForm, self).__init__(*args, **kwargs)
taxonomy_active = kwargs['instance'].taxonomy.all().values_list('id', flat=True)
for tax_type in Taxonomy.
self.fields['taxonomy_' + tax_type] = forms.MultipleChoiceField()
self.fields['taxonomy_' + tax_type].queryset = Taxonomy.objects.filter(type=tax_type)
self.Meta.fields.append('taxonomy_' + tax_type)
self.initial['taxonomy' + tax_type] = Taxonomy.objects.filter(
id__in=taxonomy_active,
type=tax_type
).values_list('id', flat=True)
class GameAdmin(admin.ModelAdmin):
form = GameAdminForm
def get_fieldsets(self, request, obj=None):
fieldsets = super(GameAdmin, self).get_fieldsets(request, obj)
for tax_type in Taxonomy.TAXONOMY_ORDER:
fieldsets[0][1]['fields'] += ['taxonomy_' + tax_type]
return fieldsets
I have two issues with this:
When I try add fields dynamicly I recieve an error
Unknown field(s) (taxonomy_FEATURE, taxonomy_PLATFORM, taxonomy_CATEGORY, taxonomy_GRAPHICS, taxonomy_GENRE, taxonomy_GAME_PROCESS) specified for Game. Check fields/fieldsets/exclude attributes of class GameAdmin.
When I try to add custom fields explicitly they are rendered blank
class GameAdminForm(forms.ModelForm):
taxonomy_PLATFORM = forms.MultipleChoiceField()
taxonomy_GAME_PROCESS = forms.MultipleChoiceField()
taxonomy_GRAPHICS = forms.ChoiceField()
taxonomy_GENRE = forms.MultipleChoiceField()
taxonomy_CATEGORY = forms.MultipleChoiceField()
taxonomy_FEATURE = forms.MultipleChoiceField()
def __init__(self, *args, **kwargs):
***__init__ stuff***
I don't have the rep for a comment, so this'll have to be an answer. I have been trying to solve the same problem for my own project, and the best solution I have found is this self-answered question by chadgh: How do I create and save dynamic fields in Django ModelAdmin?.
I have tried this method in my code. It works perfectly, and I think it does exactly what you're trying to accomplish. The only caveat is that in
def get_form(self, request, obj=None, **kwargs):
kwargs['fields'] = flatten_fieldsets(self.declared_fieldsets)
return super(PersonAdmin, self).get_form(request, obj, **kwargs)
self.declared_fieldsets is deprecated as of Django 1.7. I used self.fieldsets instead and it worked fine.
The main difficulty is that ModelAdmin normally gets the fields from the form class before it's instantiated, before the form's __init__ has executed, so it doesn't see the dynamic fields. That's why you have to override ModelAdmin.get_form.
I find out hot to solve dynamic MultipleChoiceField population with data, but still looking for proper solution of adding custom dynamic fields to ModelForm.
To populate MultipleChoiceField with existing values we need to pass choises on initialization:
forms.MultipleChoiceField(choices=choises)
To do that in dynamic way, we need to add our field to self.fields in __init__
self.fields['dynamic_field_name'] = forms.MultipleChoiceField(choices=choises)
To pass selected values:
self.initial['dynamic_field_name'] = initial_value
Complete code:
class GameAdminForm(forms.ModelForm):
dynamic_field = forms.MultipleChoiceField()
def __init__(self, *args, **kwargs):
super(GameAdminForm, self).__init__(*args, **
choises = Taxonomy.objects.all().values_list('id', 'name')
self.fields['dynamic_field'] = forms.MultipleChoiceField(choices=choises)
self.initial['dynamic_field'] = active_id_list
class Meta:
model = Game
fields = '__all__'
class GameAdmin(admin.ModelAdmin):
form = GameAdminForm
exclude = ['rating', '_id']
admin.site.register(Game, GameAdmin)

Django Admin - Filter Choice Field based on current object being edited

I am attempting to exclude the current object being edited from a choice field to select a parent object from the same model. For example:
from django import forms
from django.contrib import admin
class RelationForm(forms.ModelForm):
parent = forms.ModelChoiceField(queryset=Ingredient.objects.exclude(id=current_ingredient_id))
def save(self, commit=True):
parent = self.cleaned_data.get('parent', None)
# ...do something with extra_field here...
return super(RelationForm, self).save(commit=commit)
class Meta:
model = IngredientRelations
exclude = ['description']
#admin.register(Ingredient)
class IngredientAdmin(admin.ModelAdmin):
form = RelationForm
fieldsets = (
(None, {
'fields': ('name', 'slug', 'description', 'parent',),
}),
)
The difficult comes from getting the current object being edited and then getting its primary key in the RelationForm for the queryset argument.
I have tried using ModelAdmin.formfield_for_foreignkey and ModelAdmin.formfield_for_choice_field in IngredientAdmin with no luck.
Any ideas?
The canonical method to do this is by updating the queryset in the __init__ method using currently edited instance id.
class RelationForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(RelationForm, self).__init__(*args, **kwargs)
if self.instance.id:
self.fields['parent'].queryset = Ingredient.objects.exclude(id=self.instance.id)
class Meta:
model = IngredientRelations
exclude = ['description']
originally seen in this answer: https://stackoverflow.com/a/1869917/484127

Django How to override a child form in inlineformset_factory

I'm trying to override concept queryset in my child form, to get a custom list concepts based on the area got from request.POST, here is my list of concepts, which i need to filter based on the POST request, this lists is a fk of my child form (InvoiceDetail). is it possible to have these filters?
after doing some test when I pass the initial data as the documentation says initial=['concept'=queryset_as_dict], it always returns all the concepts, but i print the same in the view and its ok the filter, but is not ok when i render in template, so I was reading that I need to use some BaseInlineFormset. so when I test I obtained different errors:
django.core.exceptions.ValidationError: ['ManagementForm data is missing or has been tampered with']
'InvoiceDetailFormFormSet' object has no attribute 'fields'
so here is my code:
models.py
class ConceptDetail(CreateUpdateMixin): # here, is custom list if area='default' only returns 10 rows.
name = models.CharField(max_length=150)
area = models.ForeignKey('procedure.Area')
class Invoice(ClusterableModel, CreateUpdateMixin): # parentForm
invoice = models.SlugField(max_length=15)
class InvoiceDetail(CreateUpdateMixin): # childForm
tax = models.FloatField()
concept = models.ForeignKey(ConceptDetail, null=True, blank=True) # fk to override using custom queryset
invoice = models.ForeignKey('Invoice', null=True, blank=True)
views.py
class CreateInvoiceProcedureView(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
template_name = 'invoice/invoice_form.html'
model = Invoice
permission_required = 'invoice.can_check_invoice'
def post(self, request, *args, **kwargs):
self.object = None
form = InvoiceForm(request=request)
# initial initial=[{'tax': 16, }] removed
invoice_detail_form = InvoiceDetailFormSet(request.POST, instance=Invoice,
request=request)
return self.render_to_response(
self.get_context_data(
form=form,
invoice_detail_form=invoice_detail_form
)
)
forms.py
class BaseFormSetInvoice(BaseInlineFormSet):
def __init__(self, *args, **kwargs):
# call first to retrieve kwargs values, when the class is instantiated
self.request = kwargs.pop("request")
super(BaseFormSetInvoice, self).__init__(*args, **kwargs)
self.queryset.concept = ConceptDetail.objects.filter(
Q(area__name=self.request.POST.get('area')) | Q(area__name='default')
)
class InvoiceForm(forms.ModelForm):
class Meta:
model = Invoice
fields = ('invoice',)
class InvoiceDetailForm(forms.ModelForm):
class Meta:
model = InvoiceDetail
fields = ('concept',)
InvoiceDetailFormSet = inlineformset_factory(Invoice, InvoiceDetail,
formset=BaseFormSetInvoice,
form=InvoiceDetailForm,
extra=1)
How can i fix it?, what do i need to read to solve this problem, I tried to debug the process, i didn't find answers.
i try to do this:
def FooForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(FooForm, self).__init__(*args, **kwargs)
self.fields['concept'].queryset = ConceptDetail.objects.filter(area__name='default')
In a inlineformset_factory how can do it?.
After a lot of tests, my solution is override the formset before to rendering, using get_context_data.
def get_context_data(self, **kwargs):
context = super(CreateInvoiceProcedureView, self).get_context_data(**kwargs)
for form in context['invoice_detail_form']:
form.fields['concept'].queryset = ConceptDetail.objects.filter(area__name=self.request.POST.get('area'))
return context

How to make optionally read-only fields in django forms?

I have a read-only field in a django form that I sometimes want to edit.
I only want the right user with the right permissions to edit the field. In most cases the field is locked, but an admin could edit this.
Using the init function, I am able to make the field read-only or not, but not optionally read-only. I also tried passing an optional argument to StudentForm.init but that turned much more difficult that I expected.
Is there a proper way to do accomplish this?
models.py
class Student():
# is already assigned, but needs to be unique
# only privelidged user should change.
student_id = models.CharField(max_length=20, primary_key=True)
last_name = models.CharField(max_length=30)
first_name = models.CharField(max_length=30)
# ... other fields ...
forms.py
class StudentForm(forms.ModelForm):
class Meta:
model = Student
fields = ('student_id', 'last_name', 'first_name',
# ... other fields ...
def __init__(self, *args, **kwargs):
super(StudentForm, self).__init__(*args, **kwargs)
instance = getattr(self, 'instance', None)
if instance:
self.fields['student_id'].widget.attrs['readonly'] = True
views.py
def new_student_view(request):
form = StudentForm()
# Test for user privelige, and disable
form.fields['student_id'].widget.attrs['readonly'] = False
c = {'form':form}
return render_to_response('app/edit_student.html', c, context_instance=RequestContext(request))
Is that what you are looking for? By modifying your code a little bit:
forms.py
class StudentForm(forms.ModelForm):
READONLY_FIELDS = ('student_id', 'last_name')
class Meta:
model = Student
fields = ('student_id', 'last_name', 'first_name')
def __init__(self, readonly_form=False, *args, **kwargs):
super(StudentForm, self).__init__(*args, **kwargs)
if readonly_form:
for field in self.READONLY_FIELDS:
self.fields[field].widget.attrs['readonly'] = True
views.py
def new_student_view(request):
if request.user.is_staff:
form = StudentForm()
else:
form = StudentForm(readonly_form=True)
extra_context = {'form': form}
return render_to_response('forms_cases/edit_student.html', extra_context, context_instance=RequestContext(request))
So the thing is to check permissions on the views level, and then to pass argument to your form when it is initialized. Now if staff/admin is logged in, fields will be writeable. If not, only fields from class constant will be changed to read only.
It would be pretty easy to use the admin for any field editing and just render the student id in the page template.
I'm not sure if this answers your questions though.