So I have two Models that I want to relate with a ForeignKey. One of the ModelForms I want to have it's Foreign Key field pre populated before the model gets created. The info from the ForeignKey comes from a ListView (List of Cars that belong to clients) template.
MODELS.PY
class ClientCar(models.Model):
license_plate = models.CharField(max_length=20, unique=True, name='license_plate')
def__str__:
pk = self.pk
license_plate = self.license_plate
return f"pk:{pk} license_plate {license_plate}"
class CarDetail(model.Model):
car = models.ForeignKey(ClientCar, on_delete=models.CASCADE, null=False)
detail = models.CharField(max_length=40, null=False)
So the ListView template will have the basic crud of the Car model but I also want to add a "Wash button", the wash button will pass the selected Car's pk to the CarDetail Form template. It is here where I am having issues. I can Query the PK of the car from Kwargs but I can't seem to populate the Form's field with that query or have it render on the template.
VIEWS.PY
class WashService(LoginRequiredMixin, CreateView):
model = CarDetail
form_class = WashServiceForm
template_name = 'service_app/standard_wash_form.html'
def get_form_kwargs(self, *args, **kwargs):
kwargs = super(WashService, self).get_form_kwargs(*args, **kwargs)
ctd = ClientCar.objects.filter(pk=self.kwargs.get('pk')).values('license_plate')
kwargs['initial']['car'] = ctd
return kwargs
I have researched this and came to the understanding that in the Form for creating this model I have to overwrite the _ _ init _ _ function, I'm not really sure how to solve this since I don't know how to call the kwargs passed from the Listview template from the forms.py
If you can guide me with some snippets or anything I'm greatful.
Thanks in advance.
I think it makes more sense to simply change what function the ModelChoiceField uses for the choices. We can first make a subclass of ModelChoiceField for the car, to select this by license plate:
from django import forms
class CarByLicensePlateChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return obj.license_plate
Then in your WashServiceForm we can use this field:
class WashServiceForm(forms.ModelForm):
car = CarByLicensePlateChoiceField(queryset=Car.objects.all())
class Meta:
model = Car
fields = ['car', 'detail']
In your CreateView, you can then populate the car with the Car that belongs to the given primary key:
from django.shortcuts import get_object_or_404
class WashService(LoginRequiredMixin, CreateView):
model = CarDetail
form_class = WashServiceForm
template_name = 'service_app/standard_wash_form.html'
def get_form_kwargs(self, *args, **kwargs):
kwargs = super().get_form_kwargs(*args, **kwargs)
initials = kwargs.setdefault('initial', {})
intial['car'] = get_object_or_404(Car, pk=self.kwargs['pk'])
return kwargs
Related
I have a FormView with a get_initial method which I am trying to use to populate the form. I am trying to get the EmployeeTypes of the receiver of the memo as values in the form.
def get_initial(self):
initial = super(NotificationView, self).get_initial()
users = Memo.objects.filter(id=self.kwargs['pk']).values('receiver__employee_type')
initial['receiving_groups'] = users
return initial
There are 2 issues here..
This returns a Queryset which looks like: <QuerySet [{'receiver__employee_type': 'Bartender'}, {'receiver__employee_type': 'Supervisor'}]> when I really need the fields in the form to be the EmployeeType itself.
Most importantly - the form isn't even rendering these fields.
Here is the form just in case:
class MemoNotificationForm(forms.Form):
class Meta:
fields = [
'receiving_groups'
]
receiving_groups = forms.MultipleChoiceField(
required=False,
widget=forms.CheckboxSelectMultiple)
How do I populate the fields of the form?
EDIT:
class Memo(models.Model):
receiver = models.ManyToManyField(EmployeeType, related_name='memos_receiver')
class EmployeeType(models.Model):
"""Stores user employee type."""
employee_type = models.CharField(
max_length=32,
unique=True)
Having a Meta on a forms.Form doesn't do anything, this is used for ModelForms
If receiving_groups should be choices of EmployeeType then it should be a ModelMultipleChoiceField
class MemoNotificationForm(forms.Form):
receiving_groups = forms.ModelMultipleChoiceField(
EmployeeType.objects.all(),
widget=forms.CheckboxSelectMultiple
)
Then you should be passing instances, or a queryset of the model in the initial
def get_initial(self):
initial = super(NotificationView, self).get_initial()
initial['receiving_groups'] = EmployeeType.objects.filter(memo__id=self.kwargs['pk'])
return initial
EDIT:
As a ModelForm this could look like so
class MemoNotificationForm(forms.ModelForm):
class Meta:
model = Memo
fields = ('receiver', )
View:
class NotificationView(FormView);
form_class = MemoNotificationForm
def get_form_kwargs(self):
kwargs = super(NotificationView, self).get_form_kwargs()
kwargs['instance'] = get_object_or_404(Memo, id=self.kwargs['pk'])
return kwargs
While #lain Shelvington is correct in the process he used to produce the form results, I had to do a little editing to make the code operate correctly...
def get_initial(self):
initial = super(NotificationView, self).get_initial()
receivers = Memo.objects.filter(id=self.kwargs['pk']).values_list('receiver')
initial['receiving_groups'] = EmployeeType.objects.filter(employee_type=receivers)
return initial
I was making a form for creation unpublished Artist instances and then adding Artwork to the artist before publishing.
I have manager to hide published=False artists and do not know how to bypass this yet in ForeignKey.
models.py
class PublishedManager(models.Manager):
"""Returns only published Artists in queryset"""
def get_query_set(self):
qs = super(VisibleManager, self).get_query_set()
qs = qs.filter(status='published')
return qs
class Artist(models.Model):
objects = PublishedManager() # Used overall on the site to hide published=False objects
admin_objects = models.Manager() # Default manager Will be used in admin to show invisible objects too
class Artwork(models.Model):
artist= models.ForeignKey(Artist)
forms.py
class ArtworkForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ArtworkForm,self).__init(args,kwargs)
from django.db import models
self.fields['artist'].queryset = Artist.admin_objects.all()
shell
>>> form=Artwork_form(artist=180) #unpublished artist pk
>>> form.errors
Model artist with pk 180 does not exist
I need to make the form "see" the unpublished artists in FK, how can i achieve this?
Thank you.
I found the solution!!!
Original info is here http://www.hoboes.com/Mimsy/hacks/custom-managers-django-foreignkeys/
I implemented the CustomManagerForeignKey as the autor of this post had written with the one exception(otherwise in won't work properly):
usage:
class Artwork(models.Model):
artist= CustomManagerForeignKey(Artist, manager=Artist.admin_objects)
and a fix for this in the CustomManagerForeignKey:
class CustomManagerForeignKey(models.ForeignKey):
def __init__(self, *args, **kwargs):
if 'manager' in kwargs:
self.customManager = kwargs['manager'] #Here i removed the () in the end of the line
del kwargs['manager']
else:
self.customManager = None
super(CustomManagerForeignKey, self).__init__(*args, **kwargs)
I got the error as stated in the title above. I am trying to feed the form the user_id since my model requires that in order to add a 'table'. However, my use of get_form_kwargs seems to be problematic.
This is the model:
from django.db import models
from django.contrib.auth.models import User
class Vtable(models.Model):
user = models.ForeignKey(User)
table_name = models.CharField(max_length=200)
added_date = models.DateTimeField('date added')
class Vdata(models.Model):
table_id = models.ForeignKey(Vtable)
table_pk = models.IntegerField()
column_1 = models.CharField(max_length=200)
column_2 = models.CharField(max_length=200)
added_date = models.DateTimeField('date added')
This is the view:
from django.shortcuts import render
from django.http import HttpResponse
from django.views import generic
from vtables.models import Vtable
class CreateTableView(generic.CreateView):
model = Vtable
fields = ['table_name']
def get_form_kwargs(self):
# pass "user" keyword argument with the current user to your form
kwargs = super(CreateTableView, self).get_form_kwargs()
kwargs['user_id'] = self.request.user
return kwargs
The form class generated by a CreateView (or any model form for that matter) does not have an _id field for any foreign key. Instead, it has a field user which is a ModelChoiceField.
Furthermore, that logic should not be contained in your form. A form is merely a means of capturing and validating user input. Which user creates an object is not user input, and such logic should be in your view, e.g.:
class CreateTableView(generic.CreateView):
model = Vtable
fields = ['table_name']
def form_valid(self, form):
form.instance.user = self.request.user
return super(CreateTableView, self).form_valid(form)
In order to pass a custom value to the form, you'll have to create your own form class and pass that into the view. The default form class that the view creates doesn't know what to do with your user_id argument, and that's where the error comes from.
Here is an example on how to pass a custom form class, first the form class:
class MyForm(forms.ModelForm):
class Meta:
model = Vtable
def __init__(self, *args, **kwargs):
user_id = kwargs.pop('user_id') # Pop out your custom argument
super(MyForm, self).__init__(args, kwargs) # Initialize your form
# as usual
self.user_id = user_id # Add it as an instance variable
Then, in your view:
class CreateVTable(generic.CreateView):
form_class = MyForm
model = Vtable
I could explain the whole thing to you but I guess a code speaks clearer than words so:
class Skills(models.Model):
skill = models.ForeignKey(ReferenceSkills)
person = models.ForeignKey(User)
class SkillForm(ModelForm):
class Meta:
model = Skills
fields = ( 'person', 'skill')
(???)skill = forms.ModelChoiceField(queryset= SkillsReference.objects.filter(person = self.person)
I'm just guessing at how I can do it. But I hope you guys understand what I'm trying to do.
You can ovverride a form structure before you create an instance of the form like:
class SkillForm(ModelForm):
class Meta:
model = Skills
fields = ( 'person', 'skill')
In your view:
SkillForm.base_fields['skill'] = forms.ModelChoiceField(queryset= ...)
form = SkillForm()
You can override it anytime you want in your view, impottant part is, you must do it before creating your form instance with
form = SkillForm()
Assuming you are using class-based views, you can pass the queryset in your form kwargs and then replace it on form init method:
# views.py
class SkillUpdateView(UpdateView):
def get_form_kwargs(self, **kwargs):
kwargs.update({
'skill_qs': Skills.objects.filter(skill='medium')
})
return super(self, SkillUpdateView).get_form_kwargs(**kwargs)
# forms.py
class SkillForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
qs = kwargs.pop('skill_ks')
super(self, SkillForm).__init__(*args, **kwargs)
self.fields['skill'].queryset = qs
But, personally I prefer this second approach. I get the form instance on the View and than replace the field queryset before django wrap it on the context:
# views.py
class SkillsUpdateView(UpdateView):
form_class = SkillForm
def get_form(self, form_class=None):
form = super().get_form(form_class=self.form_class)
form.fields['skill'].queryset = Skills.objects.filter(skill='medium')
return form
Your code looks almost ok. Try this SkillForm:
class SkillForm(ModelForm):
skill = forms.ModelChoiceField(queryset= SkillsReference.objects.filter(person = self.person)
class Meta:
model = Skills
fields = ( 'person', 'skill')
The difference is that skill is a form's field, should not be in Meta class
EDITED
The above solution is incorrect, but this link describes how to achieve what you want:
http://www.zoia.org/blog/2007/04/23/using-dynamic-choices-with-django-newforms-and-custom-widgets/
What I would like to do is to display a single form that lets the user:
Enter a document title (from Document model)
Select one of their user_defined_code choices from a drop down list (populated by the UserDefinedCode model)
Type in a unique_code (stored in the Code model)
I'm not sure how to go about displaying the fields for the foreign key relationships in a form. I know in a view you can use document.code_set (for example) to access the related objects for the current document object, but I'm not sure how to apply this to a ModelForm.
My model:
class UserDefinedCode(models.Model):
name = models.CharField(max_length=8)
owner = models.ForeignKey(User)
class Code(models.Model):
user_defined_code = models.ForeignKey(UserDefinedCode)
unique_code = models.CharField(max_length=15)
class Document(models.Model):
title = models.CharField(blank=True, null=True, max_length=200)
code = models.ForeignKey(Code)
active = models.BooleanField(default=True)
My ModelForm
class DocumentForm(ModelForm):
class Meta:
model = Document
In regards to displaying a foreign key field in a form you can use the forms.ModelChoiceField and pass it a queryset.
so, forms.py:
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
def __init__(self, *args, **kwargs):
user = kwargs.pop('user','')
super(DocumentForm, self).__init__(*args, **kwargs)
self.fields['user_defined_code']=forms.ModelChoiceField(queryset=UserDefinedCode.objects.filter(owner=user))
views.py:
def someview(request):
if request.method=='post':
form=DocumentForm(request.POST, user=request.user)
if form.is_valid():
selected_user_defined_code = form.cleaned_data.get('user_defined_code')
#do stuff here
else:
form=DocumentForm(user=request.user)
context = { 'form':form, }
return render_to_response('sometemplate.html', context,
context_instance=RequestContext(request))
from your question:
I know in a view you can use
document.code_set (for example) to
access the related objects for the
current document object, but I'm not
sure how to apply this to a ModelForm.
Actually, your Document objects wouldn't have a .code_set since the FK relationship is defined in your documents model. It is defining a many to one relationship to Code, which means there can be many Document objects per Code object, not the other way around. Your Code objects would have a .document_set. What you can do from the document object is access which Code it is related to using document.code.
edit: I think this will do what you are looking for. (untested)
forms.py:
class DocumentForm(forms.ModelForm):
class Meta:
model = Document
exclude = ('code',)
def __init__(self, *args, **kwargs):
user = kwargs.pop('user','')
super(DocumentForm, self).__init__(*args, **kwargs)
self.fields['user_defined_code']=forms.ModelChoiceField(queryset=UserDefinedCode.objects.filter(owner=user))
self.fields['unique_code']=forms.CharField(max_length=15)
views.py:
def someview(request):
if request.method=='post':
form=DocumentForm(request.POST, user=request.user)
if form.is_valid():
uniquecode = form.cleaned_data.get('unique_code')
user_defined_code = form.cleaned_data.get('user_defined_code')
doc_code = Code(user_defined_code=user_defined_code, code=uniquecode)
doc_code.save()
doc = form.save(commit=False)
doc.code = doc_code
doc.save()
return HttpResponse('success')
else:
form=DocumentForm(user=request.user)
context = { 'form':form, }
return render_to_response('sometemplate.html', context,
context_instance=RequestContext(request))
actually you probably want to use get_or_create when creating your Code object instead of this.
doc_code = Code(user_defined_code=user_defined_code, code=uniquecode)