add foreign key fields to form - django

I'm trying to create a form from this model:
class A(models.Model):
u = models.OneToOneField(User)
and then create this form:
class AForm(ModelForm):
class Meta:
model = A
fields = ['u']
then i create an instance of that form in my view and send it to my template as a context I'll get a drop down list to choose from existing users but what i want to do is to have a text field to change my current user's first name or last name.
I'll be grateful if you could help me to change my form class to get the right result.
Thank you

You can add the first and last name fields to the AForm ModelForm in the following way:
class AForm(ModelForm):
first_name = forms.CharField(max_length=30, blank=True)
last_name = forms.CharField(max_length=30, blank=True)
class Meta:
Model = A
def __init__(self, *args, **kwargs):
super(AForm, self).__init__(*args, **kwargs)
self.fields['first_name'].initial = self.instance.u.first_name
self.fields['last_name'].initial = self.instance.u.last_name
def save(self, commit=True):
self.instance.u.first_name = self.cleaned_data['first_name']
self.instance.u.last_name = self.cleaned_data['last_name']
self.instance.u.save()
return super(AForm, self).save(commit=commit)

In this case you do not need a modelform of A but a modelform of User. You would need to set the form's instance appropriately in the view. For example,
a_record = A.objects.get_object_or_404(A, id=1)
form = self.UserForm(instance=a.u) # UserForm is a modelform of User

Related

How to pass current logged user in a form class in django

I am trying to create a form where one field is a ModelChoicefield. Im trying to populate that field with objects from a different model. I have ran into a problem as i need to get the current logged user within the form to filter the queryset. Here are the 2 models
class UserExercises(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=50)
Muscle = models.ForeignKey(Muscle, on_delete=models.CASCADE)
class Exercise(models.Model):
exercise = models.ForeignKey(UserExercises, on_delete=models.CASCADE)
weight = models.DecimalField(max_digits=6, decimal_places=3)
reps = models.PositiveIntegerField(validators=[MaxValueValidator(100)])
difficulty = models.CharField(max_length=30)
date = models.DateTimeField(default=timezone.now)
user = models.ForeignKey(User, on_delete=models.CASCADE)
And here is my form
class AddExerciseForm(forms.Form):
exercise = forms.ModelChoiceField(queryset=UserExercises.objects.filter(user=1))
class Meta:
model = Exercise
fields = ['exercise', 'weight', 'reps', 'difficulty']
As you can see i am currently hard coding a filter in the ModelChoiceField, but want to replace that with the current users Id. Is there anyway of Going about this. Im new to django so any help would be Appreciated.
My View
#login_required
def add_exercise_view(request):
if request.method == 'POST':
user_id = request.user.id
form = AddExerciseForm(user_id=user_id)
if form.is_valid():
form.save()
return redirect('myfit-home')
else:
form = AddExerciseForm()
return render(request, 'users/register.html', {'form': form})
Firstly, AddExerciseForm should extend forms.ModelForm.
To initialize form data based on some paramater, you can override __init_ method of ModelForm to update form fields (that field is exercise in this case) based on some argument/parameter (which is user_id in this case).
class AddExerciseForm(forms.ModelForm):
class Meta:
model = Exercise
fields = ['exercise', 'weight', 'reps', 'difficulty']
def __init__(self, *args, **kwargs):
user_id = kwargs.pop('user_id', None)
super(AddExerciseForm, self).__init__(*args, **kwargs)
if user_id is not None:
# update queryset for exercise field
self.fields['exercise'].queryset = UserExercises.objects.filter(user=user_id)
else:
# UserExercises.objects.none() will return an empty queryset
self.fields['exercise'].queryset = UserExercises.objects.none()
And pass the user_id while initializing the form in view:
if request.user.is_authenticated():
# get user id
user_id = request.user
form = AddExerciseForm(user_id=user_id)
override __init__ method of the Form, and pass the user as argument
def __init__(self,user,*args, **kwargs):
self.user = user
super().__init__(*args, **kwargs)
self.fields['exercise'].queryset=
UserExercises.objects.filter(user=self.user))
self.fields['exercise'].widget = forms.CheckboxSelectMultiple
class Meta:
model = Exercise
fields = ['exercise', 'weight', 'reps', 'difficulty']

Show attribute of foreign key in add_form in Django

Is it possible show an attribute of foreign key in add_form django?
For example:
#models.py
class Test(models.Model):
name = models.CharField(max_length=60, db_column='Name') # Field name made lowercase.
product=models.ForeignKey(Product, on_delete=models.DO_NOTHING)
class Product(models.Model):
id = models.AutoField(db_column='Id', primary_key=True)
description = models.CharField(max_length=60, db_column='Description')
#admin.py
class TestAdmin(admin.ModelAdmin):
fields = ['name','product','get_description']
readonly_fields = ['get_description']
def get_description(self):
return self.product.description
Naturally this code raise exception the ErrorFields in 'get_description'.
But, does exist a way to show the 'get_description' when I insert an entry of model Test?
Thanks
You can override the ModelAdmin.get_form and alter the form that it uses, to add a field of your choosing, filled with whatever you like. For example:
#admin.py
class TestAdmin(admin.ModelAdmin):
fields = ['name','product']
def get_form(self, request, obj=None, **kwargs):
if obj.product:
# self.form.base_fields is an OrderedDict(field_name=form.Field, ...)
self.form.base_fields['product_description'] = forms.CharField(required=False, initial=obj.product.description)
self.form.base_fields['product_description'].widget.attrs['readonly'] = True
return super(TestAdmin, self).get_form(request, obj, **kwargs)
That will add a readonly text input field as the last field in your "add" form. If you wanted it placed in a different spot witin the form, you'd have to work out some logic to rebuild self.form.base_fields as a new OrderedDict in exactly the order you'd like.

Django modelforms, foreignkey filter for data owned by logged in user [duplicate]

I'm trying to display a form (ModelForm) with a select field filtered by currently logged in user. The select field in this case contains a list of categories. I want to display only the categories which "belong" to the currently logged in user. The category field is a foreign key to the IngredienceCategory model.
Here is what I've come up with so far but it's giving me an error (unexpected keyword queryset). Any ideas what I'm doing wrong?
# models.py
class IngredienceCategory(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredience Categories"
def __unicode__(self):
return self.name
class Ingredience(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
category = models.ForeignKey(IngredienceCategory, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredients"
def __unicode__(self):
return self.name
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
# views.py
def home(request):
if request.user.is_authenticated():
username = request.user.username
email = request.user.email
foods = Food.objects.filter(user=request.user).order_by('name')
ingredients = Ingredience.objects.filter(user=request.user).order_by('name')
ingrcat = IngredienceCategory.objects.filter(user=request.user)
if request.method == 'POST':
form = IngredienceForm(request.POST)
if form.is_valid():
# Create an instance of Ingredience without saving to the database
ingredience = form.save(commit=False)
ingredience.user = request.user
ingredience.save()
else:
# How to display form with 'category' select list filtered by current user?
form = IngredienceForm(queryset=IngredienceCategory.objects.filter(user=request.user))
context = {}
for i in ingredients:
context[i.category.name.lower()] = context.get(i.category.name.lower(), []) + [i]
context2 = {'username': username, 'email': email, 'foods': foods, 'ingrcat': ingrcat, 'form': form,}
context = dict(context.items() + context2.items())
else:
context = {}
return render_to_response('home.html', context, context_instance=RequestContext(request))
That's happening because ModelForm does not take a queryset keyword.
You can probably achieve this by setting the queryset on the view:
form = IngredienceForm()
form.fields["category"].queryset =
IngredienceCategory.objects.filter(user=request.user)
See related question here.
Here i have another suggestion to solve the problem. You can pass request object in your form object inside view.
In view.py just pass the request object.
form = IngredienceForm(request)
In your forms.py __init__ function also add request object
from models import IngredienceCategory as IC
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
def __init__(self, request, *args, **kwargs):
super(IngredienceForm, self).__init__(*args, **kwargs)
self.fields['name'].queryset = IC.objects.filter(user=request.user)
This filter always will be applied whenever you initialize your form .

ModelForms and ForeignKeys

I got the following models:
class Project(models.Model):
name = models.CharField(max_length=50)
class ProjectParticipation(models.Model):
user = models.ForeignKey(User)
project = models.ForeignKey(Project)
class Receipt(models.Model):
project_participation = models.ForeignKey(ProjectParticipation)
Furthermore I have the following CreateView:
class ReceiptCreateView(LoginRequiredMixin, CreateView):
form_class = ReceiptForm
model = Receipt
action = 'created'
I now want a dropdown menu where the User can choose the project, the new receipt should be for. The user should only see the project he is assigned to.
How can I do that?
The simply answer is just create a model form read the docs, this is fundamental.
You may also want to look at related names, that way you can get the reverse on the FK.
class ProjectParticipation(models.Model):
user = models.ForeignKey(User)
project = models.ForeignKey(Project, related_name='ProjectParticipation')
I found a solution using a ModelChoiceField:
class ProjectModelChoiceField(ModelChoiceField):
def label_from_instance(self, obj):
return obj.project
class ReceiptForm(ModelForm):
def __init__(self, *args, **kwargs):
super(ReceiptForm, self).__init__(*args, **kwargs)
self.fields['project_participation'] = ProjectModelChoiceField(queryset= ProjectParticipation.objects)
class Meta:
model = Receipt
And then in the CreateView:
class ReceiptCreateView(...)
def get_form(self, form_class):
form = super(ReceiptCreateView, self).get_form(form_class)
form.fields['project_participation'].queryset = ProjectParticipation.objects.filter(user=self.request.user)
return form
Is there a solution to filter the query set directly in the ModelForm?

Filter select field in ModelForm by currently logged in user

I'm trying to display a form (ModelForm) with a select field filtered by currently logged in user. The select field in this case contains a list of categories. I want to display only the categories which "belong" to the currently logged in user. The category field is a foreign key to the IngredienceCategory model.
Here is what I've come up with so far but it's giving me an error (unexpected keyword queryset). Any ideas what I'm doing wrong?
# models.py
class IngredienceCategory(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredience Categories"
def __unicode__(self):
return self.name
class Ingredience(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
category = models.ForeignKey(IngredienceCategory, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredients"
def __unicode__(self):
return self.name
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
# views.py
def home(request):
if request.user.is_authenticated():
username = request.user.username
email = request.user.email
foods = Food.objects.filter(user=request.user).order_by('name')
ingredients = Ingredience.objects.filter(user=request.user).order_by('name')
ingrcat = IngredienceCategory.objects.filter(user=request.user)
if request.method == 'POST':
form = IngredienceForm(request.POST)
if form.is_valid():
# Create an instance of Ingredience without saving to the database
ingredience = form.save(commit=False)
ingredience.user = request.user
ingredience.save()
else:
# How to display form with 'category' select list filtered by current user?
form = IngredienceForm(queryset=IngredienceCategory.objects.filter(user=request.user))
context = {}
for i in ingredients:
context[i.category.name.lower()] = context.get(i.category.name.lower(), []) + [i]
context2 = {'username': username, 'email': email, 'foods': foods, 'ingrcat': ingrcat, 'form': form,}
context = dict(context.items() + context2.items())
else:
context = {}
return render_to_response('home.html', context, context_instance=RequestContext(request))
That's happening because ModelForm does not take a queryset keyword.
You can probably achieve this by setting the queryset on the view:
form = IngredienceForm()
form.fields["category"].queryset =
IngredienceCategory.objects.filter(user=request.user)
See related question here.
Here i have another suggestion to solve the problem. You can pass request object in your form object inside view.
In view.py just pass the request object.
form = IngredienceForm(request)
In your forms.py __init__ function also add request object
from models import IngredienceCategory as IC
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
def __init__(self, request, *args, **kwargs):
super(IngredienceForm, self).__init__(*args, **kwargs)
self.fields['name'].queryset = IC.objects.filter(user=request.user)
This filter always will be applied whenever you initialize your form .