I am trying to pass a kwarg from CreateView to a ModelForm so I can dynamically adjust the fields based on values in the related Parent object. Other answers have indicated that passing a kwarg by overriding get_form_kwargs in the view and catching it with kwarg.pop in the form should work, but I get an AttributeError: 'ChildForm' has no attribute 'get' when I try. Passing the kwarg into the view context works, but doesn't give me access to the value inside the form instance.
models.py:
class Parent(models.Model):
name = models.CharField(max_length=255)
details = models.CharField(max_length=255)
detailstwo = models.CharField(max_length=255, null=True)
child_mod = models.BooleanField(default=False)
slug = models.SlugField()
creator = models.ForeignKey(User)
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.name)
super(Parent, self).save(*args, **kwargs)
def __str__(self):
return self.name
class Child(models.Model):
parent = models.ForeignKey(Parent)
parent_mod = models.CharField(max_length=255)
child_name = models.CharField(max_length=255)
def __unicode__(self):
return self.child_name
views.py:
class ChildCreateView(CreateView):
model = Child
form_class = ChildForm
template_name = 'testapp/child_form.html'
def dispatch(self, *args, **kwargs):
return super(ChildCreateView, self).dispatch(*args, **kwargs)
def get_form_kwargs(self, **kwargs):
kwargs = super(ChildCreateView, self).get_form_kwargs()
parent = get_object_or_404(Parent, slug=self.kwargs['parent_slug'])
kwargs['parent'] = parent
return kwargs
def get_context_data(self, **kwargs):
context = super(ChildCreateView, self).get_context_data(**kwargs)
parent = get_object_or_404(Parent, slug=self.kwargs['parent_slug'])
context['parent'] = parent
return context
def form_valid(self, form):
child = form.save(commit=False)
parent_slug = form.data['parent_slug']
parent = get_object_or_404(Parent, slug=parent_slug)
child.parent = parent
return super(ChildCreateView, self).form_valid(form)
def get_success_url(self):
return reverse('testapp:parent_view', kwargs={'slug': self.object.parent.slug})
forms.py:
class ChildForm(forms.ModelForm):
class Meta:
model = Child
fields = ['parent', 'parent_mod', 'child_name']
def __init__(self, *args, **kwargs):
self.parent_object = kwargs.pop('parent')
assert isinstance(self.parent_object, Parent)
super(ChildForm, self).__init__(self, *args, **kwargs)
self.fields["parent_slug"] = forms.CharField(widget=forms.HiddenInput())
if not self.parent_object.child_mod:
del self.fields['parent_mod']
Full Traceback is here: http://dpaste.com/2QBMRJX
Related
class Book(models.Model):
description = models.CharField(max_length=10)
pdf = models.FileField(upload_to='books/pdfs/')
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.description
def delete(self, *args, **kwargs):
self.pdf.delete()
super().delete(*args, **kwargs)
#forms.py
class BookForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(BookForm, self).__init__(*args, **kwargs)
class Meta:
model = Book
fields = ('description', 'pdf', 'user')
When i run an application it shows all the users, I want to restrict to only current user who is logged in.
I can't figure out why my validators doesn't work at all.
Form is not invalid
Model doesn't raise ValidationError when being saved
For input: "123456sdad"
I have a model which has broker_ico field:
REGEX_ICO = r"\d{6,8}"
broker_ico = models.CharField(max_length=100, verbose_name='IČO',
validators=[RegexValidator(REGEX_ICO)])
I've overwritten save method:
def save(self, **kwargs):
print('full clean')
self.full_clean()
super().save(**kwargs)
Moreover the form is a ModelForm:
class BusinessCaseDocumentForm(ModelForm):
class Meta:
model = BusinessCaseDocument
exclude = ['id','business_case']
def __init__(self, *args, **kwargs):
super(BusinessCaseDocumentForm, self).__init__(*args, **kwargs)
for field_name, field in self.fields.items():
fs_helpers.add_widget_attribute('class', 'form-control', field)
UpdateView:
class BusinessCaseDocumentUpdateView(SuccessMessageMixin, UpdateView):
model = BusinessCaseDocument
form_class = BusinessCaseDocumentForm
template_name = "business_cases/businesscase_documents/create.html"
success_message = "Podklad k obchodnému prípadu bol upravený"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['businesscase'] = self.object.business_case # TODO self.businesscase set
return context
def get_success_url(self):
return reverse("business_cases:list")
Can you see where is the problem?
I have a form where I am trying to limit the choices which appear in the field 'question' (In this case, I only want questions which the user has created). This method has worked with other forms, but it doesn't work this time - probably because it is a ModelFormset rather than just a ModelForm
The exact error is - 'NoneType' object has no attribute 'username', which I suspect means that the 'user' object is not being passed to AnswerForm's constructor. Problem is, I have no idea why it's not being passed
#views.py
def add_answer(request):
a_form = modelformset_factory(Answer, form=AnswerForm(user=request.user), fields='__all__', min_num=2, max_num=4, validate_min=True)
if request.method == "POST":
form = a_form(request.POST)
if form.is_valid():
#Do something
return render(request, 'site/addanswer.html', {'a_form': a_form})
#forms.py
class AnswerForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(AnswerForm, self).__init__(*args, **kwargs)
self.fields['question'] = forms.ModelChoiceField(queryset=Question.objects.filter(user__username=self.user.username))
class Meta:
model = Answer
fields = ('question', 'answer', 'correct')
#models.py
class Question(models.Model):
user = models.ManyToManyField(User)
test = models.ForeignKey(Test, on_delete=models.CASCADE)
question = models.TextField(max_length=1000)
def __str__(self):
return "{0}".format(self.question)
class Answer(models.Model):
question = models.ForeignKey(Question)
answer = models.TextField(max_length=1000)
correct = models.BooleanField()
def __str__(self):
return "{0}".format(self.answer)
Maybe self.user get overwritten by super. Also there is no need to query user.username.
#forms.py
class AnswerForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super(AnswerForm, self).__init__(*args, **kwargs)
self.fields['question'] = forms.ModelChoiceField(queryset=Question.objects.filter(user=user))
class Meta:
model = Answer
fields = ('question', 'answer', 'correct')
I have two models (OK 3 models since AssignedAsset is a subclass of Asset), one that tracks assets and another that tracks the history of owners for that asset. When I create a new asset using CreatView I would like to automatically have it create a History record as well.
models.py
class Asset(models.Model):
make = models.CharField(max_length=100)
model = models.CharField(max_length=100)
serial_number = models.CharField(max_length=100)
po = models.ForeignKey('purchaseorders.PurchaseOrder', default=None, blank=True, null=True)
location = models.ForeignKey('locations.Plant')
slug = models.SlugField(blank=True, unique=True)
def __str__(self):
return self.slug
def save(self):
forslug = "{0.make}-{0.model}-{0.serial_number}".format(self)
self.slug = slugify(forslug)
super(Asset, self).save()
class AssignedAsset(Asset):
user = models.ForeignKey(User)
def __str__(self):
return self.slug
class AssignedHistory(models.Model):
assset = models.ForeignKey('Asset')
user = models.ForeignKey(User)
date = models.DateField()
slug = models.SlugField(blank=True, unique=True)
def __str__(self):
return self.slug
def save(self):
forslug = "{0.asset}-{0.date}".format(self)
self.slug = slugify(forslug)
super(AssignedHistory, self).save()
Here is my view.
class NewAssignedAsset(CreateView):
form_class = AssignedAssetForm
template_name = 'createassignedasset.html'
success_url = '/assets'
And my forms.py
class AssignedAssetForm(forms.ModelForm):
class Meta:
model = AssignedAsset
fields = ['make', 'model', 'serial_number', 'location', 'user', 'po']
def __init__(self, *args, **kwargs):
super(AssignedAssetForm, self).__init__(*args, **kwargs)
#Filter out PO's that have packingslips (otherwise you will quickly have a ridicously big drop-down of every PO in the system)
self.fields['po'] = forms.ModelChoiceField(required=False, queryset=PurchaseOrder.objects.filter(packing_slip=''))
I thought maybe I could have it create the history when it gets the success URL, so I tried this in my view:
import time
def today():
return time.strftime ("%m/%d/%Y")
class NewAssignedAsset(CreateView):
form_class = AssignedAssetForm
template_name = 'createassignedasset.html'
def get_success_url(self):
history = AssignedHistory.objects.create(assset=self.object, user=self.object.user, date=today())
return '/assets'
But this throws a TypeError:
save() got an unexpected keyword argument 'force_insert'
Anything that would point me in the right direction would be appreciated.
You can do it at multiple levels(DB level, form level).
In your case, I'll say you just need to override the save() of your AssignedAssetForm. (Assuming you set user in context of form)
def save(self, *args, **kwargs):
assigned_asset = super(AssignedAssetForm, self).save(*args, **kwargs)
user = self.context.get(u'user')
if user:
assigned_asset_history = AssignedHistory(asset=assigned_asset, user=user, date=datetime.date.today())
assigned_asset_history.save()
return assigned_asset
** I am not sure about the context part, you may have to look into how to use user in form.
You should write your Asset.save() and AssignedHistory.save() as:
def save(self, **kwargs):
...
super(YourModel, self).save(**kwargs)
...
Note the **kwargs. They allow you to accept optional parameters (and a Model.save() has a few).
I am trying to create a query to grab the pk of the current post from the database. Then set it as the foreign key of the new post. I am using formview, and the model I am trying to retrieve the 'id' from is called Projects. Id is the primary key of the model Projects.
How would I be able to go about this?
pk=5 because I didn't know how to get the current one.
views.py
class ProjectDetailToDoForm(FormView):
model = ProjectsToDo
form_class = ProjectToDoForm
success_url = '../..'
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
return super(ProjectDetailToDoForm, self).dispatch(request, *args, **kwargs)
def form_valid(self,form):
self.object = form.save(commit=False)
self.object.project = Projects.objects.get(pk=5)
self.object.save()
return super(ProjectDetailToDoForm, self).form_valid(form)
class ProjectDetail(generic.DetailView):
model = Projects
context_object_name = 'indprojects'
template_name = 'projectpage.html'
def get_context_data(self, *args, **kwargs):
context = super(ProjectDetail, self).get_context_data(*args, **kwargs)
context['todolist'] = ProjectsToDo.objects.order_by('project_tododate')
context['todoform'] = ProjectToDoForm()
context['form'] = ProjectForm(instance=Projects.objects.get(slug=self.kwargs['slug']))
return context
def get_queryset(self):
return Projects.objects.filter(user=self.request.user)
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
return super(ProjectDetail, self).dispatch(request, *args, **kwargs)
models.py
class Projects(models.Model):
user = models.ForeignKey(User)
slug = models.SlugField()
project_title = models.CharField(max_length=30)
project_shortdesc = models.CharField(max_length=248)
project_desc = models.TextField()
def save(self):
super(Projects, self).save()
date = datetime.date.today()
self.slug = '%i%i%i%s' % (
date.year, date.month, date.day, slugify(self.project_title)
)
super(Projects, self).save()
class ProjectsToDo(models.Model):
project_tododate = models.DateField()
project_tododesc = models.TextField(max_length = 500)
project = models.ForeignKey(Projects)
def __unicode__(self):
return '%s %s' % (self.project_tododesc, self.project_tododate)
I am guessing that you define the active project using the url. In that case, you can do something like this:
urls.py
url(r'^(?P<project_slug>[\w-]+)/add_todo/$',
views.ProjectDetailToDoForm.as_view(),
name='add_todo',
),
...
view
def form_valid(self, form):
self.object = form.save(commit=False)
project = Project.objects.get(slug=self.kwargs["project_slug"])
self.object.project = project
self.object.save()
return super(ProjectDetailToDoForm, self).form_valid(form)