Im trying to create a form that will show a list of checkboxes based on a models items. Then also to be able to filter this list if required.
However I am getting the below error and am not sure as to why?
error:
File "/usr/local/lib/python3.6/site-packages/django/forms/forms.py" in errors
174. if self._errors is None:
Exception Type: AttributeError at /sites/site/auto_gen_subnets/7
Exception Value: 'AutoSubnetForm' object has no attribute '_errors'
forms.py
class AutoSubnetForm(forms.Form):
subnet_type_data = SiteTypes.objects.all()
def __init__(self, *args, **kwargs):
self.site_type = kwargs.pop("site_type")
# get site type if set and filter against it
if self.site_type:
subnet_type_data = SiteTypes.objects.filter(site_type=self.site_type)
# create list for types
subnet_types = []
for stype in subnet_type_data:
# add tuple for each type
subnet_types.append((stype.id,stype.site_type))
subnets = forms.ChoiceField(
choices=subnet_types,
widget = forms.Select(
attrs = {'class': 'form-control'}
)
)
views.py:
#login_required
#user_passes_test(lambda u: u.has_perm('config.add_subnet'))
def auto_gen_subnets(request, site_id):
#generate_subnets(site_id)
from config.models import SubnetTypes
site_data = get_object_or_404(SiteData.objects.select_related('site_type'),pk=site_id)
subnets = None
if request.method == 'GET':
form = AutoSubnetForm(site_type=site_data.site_type)
else:
# A POST request: Handle Form Upload
form = AutoSubnetForm(request.POST)
# If data is valid, proceeds to create a new post and redirect the user
if form.is_valid():
subnets = form.cleaned_data['subnets']
return render(request, 'sites/generate_subnets.html', {
'data': subnets,
'subnet_form': form,
'SiteName' : site_data.location,
'SiteID' : site_id,
}
)
You override the init method. So you should return it to your superclass.
def __init__(self, *args, **kwargs):
self.site_type = kwargs.pop("site_type")
# get site type if set and filter against it
if self.site_type:
subnet_type_data = SiteTypes.objects.filter(site_type=self.site_type)
super(AutoSubnetForm, self).__init__(*args, **kwargs)
You do not return your _errors. So it does not know about any other data except the ones you provide while overriding. If you want all you should return it to superclass. That should cause it. The code above should fix it.
Related
I have a website that allows users to submit the same form several times (but with different input) but also allows them to view each form they submitted and edit it.
The problem is with the "key_name" field. If I view an already submitted form (edit_keydef) then there is the ValidationError message with the field highlighted in red, when in fact my intention is for that ValidationError to occur only when they are about to submit but have provided the same name as another form. (The view used when filling in the form is different to that when editing the form - as with the editing there's more fields) but both views use the same model.
I don't understand why DJango runs the clean() function when the form has already been sumbitted, i.e. the validation has already occurred and the data has already been submitted
views.py
def edit_keydef(request, id):
data = {'title': 'View or Edit Key Definition'}
template = 'generate_keys/edit_keydef.html'
keydef = get_object_or_404(KeyDefinition, pk=id)
if request.method == "POST":
keydetails_form_instance = KeyDefDetailsForm(request.POST, instance=keydef)
if 'Save' in request.POST:
if keydetails_form_instance.is_valid():
if keydetails_form_instance.cleaned_data['encoded_activation_key']:
activation_obj = Activation()
result = activation_obj.GenerateKey(keydef)
keydetails_form_instance.save()
return redirect('/key/edit/' + str(id))
else:
log.debug(keydetails_form_instance.errors)
if 'Generate' in request.POST:
if keydetails_form_instance.is_valid():
activation_obj = Activation()
result = activation_obj.GenerateKey(keydef)
if "error" in result:
data['error_msg'] = format_html(
'<p>Failed to generate activation key because:</p>'
+ '<p>'
+ str(result['error'])
+ '</p>'
)
else:
keydetails_form_instance.save()
return redirect('/key/edit/' + str(id))
else:
log.debug(keydetails_form_instance.errors)
else:
keydetails_form_instance = None
if not id:
keydetails_form_instance = KeyDefDetailsForm()
else:
# Retrieve a previously saved form.
try:
keydetails = KeyDefinition.objects.get(pk=id)
keydetails_form_instance = KeyDefDetailsForm(keydetails.to_dict(),
instance = keydetails)
except KeyDefinition.DoesNotExist as e:
return redirect('/record_does_not_exist')
# Form instance has been saved at this stage so update the variant list.
data['form'] = keydetails_form_instance
data['set_product_options_url'] = reverse_lazy('set_product_options', kwargs={'id':id})
return render(request, template, data)
models.py
class KeyDefinition (models.Model) :
...
def clean (self) :
....
# ensure that each user can't have two keys with the same name
key_name_exists = KeyDefinition.objects.filter(key_name=self.key_name, developer_email=self.developer_email)
if key_name_exists:
raise ValidationError (
{'key_name' : ['This Key Name already exists']}
)
class Meta:
verbose_name = "Key Definition"
unique_together = [['key_name', 'developer_email']]
forms.py
class KeyDefDetailsForm (ModelForm) :
def __init__(self, *args, **kwargs) :
super(ModelForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_method = 'post'
self.fields['version'].widget.attrs['class'] = "major_minor"
def update_variants(self, keydef_obj):
self.fields.update({
'feature_variant': forms.ModelChoiceField(
widget = Select(),
queryset = FeatureVariant.objects.filter(product__feature_name=keydef_obj.feature),
required = False,
label = "Product"
)
})
def update_available_hosts(self, keydef_obj):
# only show hosts that have been created by this user
self.fields.update({
'available_hosts': forms.ModelChoiceField(
empty_label=None,
initial = AvailableHosts.objects.get(host_label=keydef_obj.host_label).id,
widget=Select(),
queryset = AvailableHosts.objects.filter(host_developer_email=keydef_obj.developer_email),
required = False,
label = "Available Hosts"
)
})
class Meta :
model = KeyDefinition
fields = '__all__'
widgets = {
'customer_name' : TextInput(),
'host_method' : TextInput(),
'issue_date' : TextInput(attrs={'type':'date'}),
'expiry_date': TextInput(attrs={"type":"date"}),
'available_hosts' : Select(),
'country' : Select(),
'feature' : Select(),
'activation_request' : HiddenInput(),
'hostid_provision' : HiddenInput(),
}
The KeyDefinition indeed exists: exactly the one you are editing matches. The clean() method will always run when validating a form. That is why you should exclude that object, so with:
class KeyDefinition(models.Model):
def clean(self):
# …
# ensure that each user can't have two keys with the same name
key_name_exists = (
KeyDefinition.objects.filter(
key_name=self.key_name, developer_email=self.developer_email
)
.exclude(pk=self.pk)
.exists()
)
if key_name_exists:
raise ValidationError({'key_name': ['This Key Name already exists']})
return super().clean()
We here thus exclude our own object with .exclude(pk=self.pk).
Note: It is more efficient to use .exists() [Django-doc]
than to check the truthiness of a QuerySet, since this will not load the records from the queryset, and thus will minimize the bandwidth used between the database
and the Django/Python layer.
When I use this resources.py inside Django admin everything works fine. However, when I do it on my custom view page there is an issue that popped with the **kwargs user auto-populate.
The error must be in my view as it's not passing the **kwargs but I'm not sure how to solve it. Where should I be passing this information?
KeyError at /import/
'user'
C:\Users\winkl\tj3\venv\lib\site-packages\import_export\resources.py in import_row
self.after_import_instance(instance, new, **kwargs) …
C:\Users\winkl\tj3\portfolios\resources.py in after_import_instance
instance.created_by = kwargs['user']
resources.py
class EntryResource(resources.ModelResource):
symbol = fields.Field(
attribute="symbol",
column_name="symbol",
widget=SymbolWidget(Symbol, 'name'),
)
date = fields.Field(
attribute="date",
column_name="date",
widget=widgets.DateTimeWidget(format="%Y-%m-%d %H:%M:%S"),
)
class Meta:
model = Entry
fields = ('symbol', 'date', 'id', 'amount', 'price', 'fee', 'entry_type', 'reg_fee',)
import_order = fields
skip_unchanged = False
report_skipped = True
def after_import_instance(self, instance, new, row_number=None, **kwargs):
print(f' Kwargs: {kwargs}')
instance.created_by = kwargs['user']
def after_save_instance(self, instance, using_transactions, dry_run):
pass
view.py
#login_required
def import_data(request):
if request.method == 'POST':
trade_resource = EntryResource()
dataset = Dataset()
new_trades = request.FILES['importData']
imported_data = dataset.load(new_trades.read().decode('utf-8'),format='csv')
result = trade_resource.import_data(dataset, dry_run=True, raise_errors=True)
if result.has_errors():
messages.error(request, 'Uh oh! Something went wrong...')
else:
# Import now
trade_resource.import_data(dataset, dry_run=False)
messages.success(request, 'Your words were successfully imported')
return render(request, 'dashboard/import.html')
Obviously you are getting the error because there is no entry with key 'user' in the kwargs dict.
If you look at the source, you can see that the kwargs get passed down the call stack into after_import_instance().
In your case, at no point is there ever a user entry in the kwargs dict.
However, if you pass this value in to the import_data() call then it can be retrieved and used:
your_user = load_user() # implement this as required
result = trade_resource.import_data(dataset, dry_run=True, raise_errors=True, user=your_user)
I'm trying to create a custom field for my M2M field in my ModelForm. ConnectedTo is the many to many field. Code below:
views:
def addPartForm_Create(request, site, subtype):
siteselected = site
sitenumber = str(site)
print(sitenumber)
subtypeselected = Subtype.objects.get(SubtypeID = subtype)
if request.method == 'POST':
form = addPartForm(request.POST, sitenum=sitenumber)
if form.is_valid():
obj = form.save(commit=False)
obj.SiteID = Site.objects.get(SiteID = siteselected)
obj.Subtype = subtypeselected
obj.save()
form.save_m2m()
return redirect('/sites/'+str(site))
else:
form = addPartForm()
return render(request, 'myproj/addPart.html', {'form': form, 'SiteNo': Site.objects.get(SiteID = siteselected).SiteID, 'subtype': subtypeselected})
forms:
class addPartForm(forms.ModelForm):
class Meta:
model = Part
fields = ('Comment', 'Location', 'ConnectedTo', 'BatchNo', 'Manufacturer', 'Length', 'InspectionPeriod')
labels = {"BatchNo": "Batch Number", "InspectionPeriod": "Inspection Period"}
def __init__(self, *args, **kwargs):
super(addPartForm, self).__init__(*args, **kwargs)
sitenum = kwargs.pop('sitenum')
self.fields["ConnectedTo"].widget = forms.CheckboxSelectMultiple()
self.fields["ConnectedTo"].queryset = Part.objects.filter(SiteID = sitenum)
I get KeyError when I try to pass sitenum from view to form. I know I could set a default value None but I don't want it to ever display none. I need to always have a sitenum sent here. Am I passing this wrong?
You need to pass the kwarg sitenum to all instances of your form, you aren't doing this when the request is GET. I'd also move all kwargs.pop() above the super calls to ensure it doesn't conflict with kwargs that the super might be expecting.
I am in the process of implementing a feature to allow users to query, through a set of conditional drop boxes, to find other users by grouping. For example, my drop downs chain from Departments->Teams->Users. I have that part setup correctly, but I can't figure out how to use the data from the last (Users) to either send it to my view or redirect the user to a URL. If the user is test I want to send them to http://example.com/user/test or I want to send them to my view which accepts user as a parameter. I have a FormView, form, and 2 functions which work with Ajax to get the conditional drop boxes. See below for my code:
Views.py
class UserQuery(FormView):
template_name = 'people/users_form.html'
model = People
form_class = UsersListForm
def load_teams(request):
department = request.GET.get('department')
teams = Teams.objects.filter(department=department).order_by('team')
return render(request, 'people/team_dropdown_list_options.html', {'teams': teams})
def load_users(request):
team = request.GET.get('team')
people = People.objects.filter(team=team).order_by('people')
return render(request, 'people/people_dropdown_list_options.html', {'people': people})
def load_one(request):
selected_person = request.GET.get('people')
one = People.objects.get(id=selected_person).people
return render(request, 'index.html', {'username': one})
So load_one() has the data I need. It has the username that I want to pass to http://example.com/user/{} -- I have a view already for that URL, but I need to figure out how to get them there any the form is submitted. Right now it seems like I am using the FormView incorrectly as it wants to create something, rather than just query.
Forms.py
class PeopleListForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['team'].queryset = Teams.objects.none()
self.fields['people'].queryset = People.objects.none()
if 'department' in self.data:
try:
department_id = int(self.data.get('department'))
self.fields['team'].queryset = Teams.objects.filter(department=department_id).order_by('team')
except (ValueError, TypeError):
pass
elif self.instance.pk:
self.fields['team'].queryset = self.instance.department.team_set.order_by('team')
if 'team' in self.data:
try:
team_id = int(self.data.get('team'))
self.fields['people'].queryset = Employees.objects.filter(team=team_id).order_by('people')
except (ValueError, TypeError):
pass
elif self.instance.pk:
self.fields['people'].queryset = self.instance.department.team_set.order_by('people')
if 'people' in self.data:
try:
people_id = int(self.data.get('people'))
self.fields['people'].queryset = People.objects.get(id=people_id).people
except (ValueError, TypeError):
pass
elif self.instance.pk:
self.fields['people'].queryset = self.instance.team.people
class Meta:
model = People
fields = ('department', 'team', 'people')
urls.py
urlpatterns = [
url(r'^query/$', EmployeeSurveyQuery.as_view(), name='survey-options'),
url(r'^ajax/load_teams/$', load_teams, name='ajax-load-teams'),
url(r'^ajax/load_users/$', load_users, name='ajax-load-users'),
url(r'^ajax/load_one/$', load_one, name='ajax-load-one'),
url(r'^user/(?P<username>[\w.#+-]+)/$', user_page, name='user-page'),
]
I am trying to implement an MVC like pattern in my views.
Basically I have a base View which serves a normal type, and type1 view and type2 view that extends the base view.
I also have a controller and a model to get and set data.
I need to pass this model over to the view.
But I always get a KeyError when I access the kwargs dict from the view.
I am new to django and python. So please don't mind if I am missing something obvious.
Here is the code
class Controller():
NORMAL, TYPE1 , TYPE2 = (0,1,2)
#classmethod
def controller_init(cls): #entry point to view. Called in urls.py
def view(request,slug,*args,**kwargs):
self = cls()
self.request = request
self.slug = slug
self.args = args
self.kwargs = kwargs
return self.start()
return view
def start(self):
modal = Modal()
self.kwargs['modal'] = modal
modal.init(self.slug)
ptype = modal.getPtype()
return self.showView(ptype)
def showView(self,viewType):
if(viewType == self.NORMAL):
view_handler = View1.as_view()
elif(projectType == self.TYPE1):
view_handler = ExtendedView1.as_view()
else:
view_handler = ExtendedView2.as_view()
return view_handler(self.request,self.args,self.kwargs)
Here is my view :
from django.views.generic.base import TemplateView
class View1(TemplateView):
template_name = "view_detail.html"
def get_context_data(self, **kwargs):
context = super(View1, self).get_context_data(**kwargs)
self.modal = kwargs['modal']
context['pid'] = self.modal.getPID()
context['title'] = "This is a normal"
return context
When I run the page, I get a KeyError saying the key modal is not defined.