Forms for Intermediate Models - django

I'm having trouble saving data for an intermediate field. I have forms (unlisted here) to create Stocks and Portfolios. Now I am trying to build a Membership form to link the two. I followed the django documentation and the code works perfectly in the the API. Then in the views, I can the membership to save fine (I pulled it up in the API afterwards to check), but it doesn't make the make the connection between the Stock and Portfolio. After the membership form, there is a membership object saved in my database but it is not connecting the portfolio to the stock.
Thanks in advance.
Here are my models:
class Stock(models.Model):
name = models.CharField(max_length=160)
ticker = models.SlugField(max_length=20)
created = models.DateTimeField(auto_now_add=True, blank=True)
class Portfolio(models.Model):
name = models.CharField(max_length=160)
stocks = models.ManyToManyField(Stock, through='Membership')
class Membership(models.Model):
stock = models.ForeignKey(Stock)
portfolio = models.ForeignKey(Portfolio)
shares = models.IntegerField(default=0)
class StockForm(forms.ModelForm):
class Meta:
model = Stock
exclude = []
class PortfolioForm(forms.ModelForm):
class Meta:
model = Portfolio
exclude = []
class MembershipForm(forms.ModelForm):
class Meta:
model = Membership
exclude = []
And here are my views.py:
def membership_form(request):
if request.method == 'POST':
form = MembershipForm(request.POST, request.FILES)
if form.is_valid():
new_obj = form.save(commit=False)
new_obj.save()
membership = Membership.objects.create( portfolio=new_obj.portfolio ,stock=new_obj.stock)
membership.save()
return HttpResponseRedirect(reverse('portfolios', args=[]))
else:
form = MembershipForm(None)
return render(request, 'portfolio/form.html', {'form': form})
I based the views off Django m2m form save " through " table .

Write a custom form and save the relationship inside it.
I have taken the example as classes of of email address associated with an email that i wanted to send
class SendForm (forms.ModelForm) :
To = forms.ModelMultipleChoiceField(
queryset=EmailId.objects.filter(),required=False,widget=FilteredSelectMultiple(('To'), False))
def __init__(self, *args, **kwargs):
initial = kwargs.setdefault('initial', {})
if 'instance' in kwargs:
initial['To']=kwargs['instance'].To.all()
super(SendForm, self).__init__(*args, **kwargs)
def save(self, commit=True):
instance = forms.ModelForm.save(self, commit)
instance.save(send=0)
es = [e for es in self.cleaned_data['To']]
for relation in instance.To.all():
instance.To.remove(relation)
for e in es:
instance.To.add(e)
instance.save(send=1)
return instance
class Meta:
model =SentEmail

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']

How to create or update value to relationship model

I want to save the Portfolio products details in PortfolioProducts model in django
I have models like below:
class Product(models.Model):
name = models.CharField(max_length=255,null=True, verbose_name ='Name')
class Portfolio(models.Model):
name = models.CharField(max_length=100, blank=True, null=True, verbose_name ='Name')
class PortfolioProducts(models.Model):
portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE, verbose_name ='Portfolio')
product = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name ='Product')
Portfolio form:
class PortfolioForm(forms.ModelForm):
class Meta:
model = Portfolio
fields = ['name']
My view file:
def edit(request):
portfolio_form = PortfolioForm
if request.method=="POST":
portfolio_id=request.POST.get('portfolio_id')
portfolio_detail = Portfolio.objects.get(pk=portfolio_id)
pform = portfolio_form(request.POST, instance=portfolio_detail)
if pform.is_valid():
portfolio = pform.save(commit = False)
portfolio.save()
products=request.POST.getlist('product_id[]')
for product in products:
ppform = PortfolioProducts(product_id=product, portfolio_id=portfolio_id)
port_product = ppform.save()
I am trying to save and update the Portfolio products like this, but is adding products to portfolio multiple time.
Well, you don't need to update PortfolioProduct for updating Portofilio. Because even if you update Portfolio, its primary key remains same as before. So the relationship remains the same.
But, in your case, if PortofolioProduct does not exist for a product in products and Portfolio object, then you can create one like this:
for product in products:
ppform, _ = PortfolioProducts.objects.get_or_create(product_id=product, portfolio_id=portfolio_id)
Update
From comments: you need to either remove def save(self): methods from you Model(Because you are not doing anything particular in those save methods) or if intend to keep you save() methods, then you need to call the super properly, like this:
class Product(models.Model):
name = models.CharField(max_length=255,null=True, verbose_name ='Name')
def save(self, *args, **kwargs):
super(Product, self).save(*args, **kwargs)
class Portfolio(models.Model):
name = models.CharField(max_length=100, blank=True, null=True, verbose_name ='Name')
def save(self, *args, **kwargs):
super(Portfolio, self).save(*args, **kwargs)
class PortfolioProducts(models.Model):
portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE, verbose_name ='Portfolio')
product = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name ='Product')
def save(self, *args, **kwargs):
super(PortfolioProducts, self).save(*args, **kwargs)
Yes, I also got stuck with the same issue in my django project. The thing it does in my case was everytime the user tries to update his/her profile, it created a new one, this is because of the Foreign Key to it. I fixed the issue by deleting the previous user profile (in your case it's portfolio) every time the user updates it.
class UserEdit(TemplateView):
template_name = 'accounts/homee.html'
def get(self, request):
form = UserProfilee()
ppp = UserProfile.objects.get(user=request.user)
return render(request, self.template_name, {'form': form, 'ppp': ppp})
def post(self, request):
form = UserProfilee(request.POST, request.FILES)
pppp = UserProfile.objects.get(user=request.user)
if form.is_valid():
post = form.save(commit=False)
post.user = request.user
if not post.image:
post.image = pppp.image
UserProfile.objects.filter(user=post.user).delete()
post.save()
return redirect('/home/homepage/')
args = {'form': form}
return render(request, self.template_name, args)
As you see,I filter the user and delete the user profile whenever user updates his/her profile thus leaving only 1 user profile.

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 .

Populate foreign key fields using data from form based on ModelForm

I have set up my models as shown below:
Models.py
class Customer(models.Model):
CustomerID = models.CharField(max_length=50)
Name = models.CharField(max_length=50)
Mobile = models.CharField(max_length=15)
def __unicode__(self):
return self.CustomerID
class orderHistory(models.Model):
BookingNumber = models.CharField(max_length=50)
customer = models.ForeignKey(Customer)
def __unicode__(self):
return self.BookingNumber
I have also used ModelForm to setup my forms.py:
class customerForm(ModelForm):
class Meta:
model = Customer
exclude = ['CustomerID']
The function in my views.py is something like this:
if request.method == 'POST':
form = customerForm(request.POST or None)
# check whether it's valid:
if form.is_valid():
instance = form.save()
if not instance.CustomerID:
instance.CustomerID = 'DHOBI' + instance.Mobile
instance.save()
return HttpResponseRedirect('/data/test')
else:
form = customerForm()
return render(request, "data/index.html",{'form':form})
My question is, after getting the data for the Customer model, how do I fill the data in BookingNumber in the database? I mean, if I want to set the booking number as Mobile + Today's Date and time.
How do I do that.
import datetime
bn = "%s-%s" % (instance.Mobile,str(datetime.datetime.now())
order_hist = orderHistory.objects.create(customer=instance, BookingNumber=bn)
Just note on naming of classes: give Classes Uppercase names and the attributes lowercase. You have it in reverse.

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 .