I am trying to add data in a model using django forms but getting
Direct assignment to the forward side of a many-to-many set is prohibited. Use tag.set() instead.
Please help me solve this. I am using multiselect field to send data to views.py.
models.py
class Tags(models.Model):
tag = models.CharField(max_length=100)
def __str__(self):
return self.tag
class Meta:
verbose_name_plural = 'Tags'
class Post(models.Model):
...
author = models.ForeignKey(User, verbose_name='Author', on_delete=models.CASCADE)
feature_image = models.ImageField(upload_to='blog/', verbose_name='Add Feature Image')
tag = models.ManyToManyField(Tags, related_name='post_tags', verbose_name='Add Tags')
def __str__(self):
return self.title
forms.py
class PostForm(forms.ModelForm):
...
class Meta:
model = models.Post
fields = [...]
views
def adminNewPostView(request):
form = forms.PostForm()
if request.method == 'POST':
...
tags = request.POST.getlist('tagName')
form = forms.PostForm(request.POST, request.FILES)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.category = cat
if subcat:
post.sub_categories = subcat
if subfil:
post.filter_option = subfil
add_tags = models.Tags.objects.filter(tag__in=tags)
for tl in add_tags:
post.tag = tl # Getting Error here
post.save()
return HttpResponseRedirect(reverse('blog_app:index'))
Error
Direct assignment to the forward side of a many-to-many set is prohibited. Use tag.set() instead.
views.py
def adminNewPostView(request):
form = forms.PostForm()
if request.method == 'POST':
...
tags = request.POST.getlist('tagName')
form = forms.PostForm(request.POST, request.FILES)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.category = cat
if subcat:
post.sub_categories = subcat
if subfil:
post.filter_option = subfil
post.save()
add_tags = models.Tags.objects.filter(tag__in=tags)
for tl in add_tags:
post.tag.add(tl) # new
return HttpResponseRedirect(reverse('blog_app:index'))
to learn more about this please refer to https://docs.djangoproject.com/en/dev/ref/models/relations/ or here https://docs.djangoproject.com/en/2.2/topics/db/examples/many_to_many/#many-to-many-relationships
there is also an other way of doing it.like this
def adminNewPostView(request):
form = forms.PostForm()
if request.method == 'POST':
...
tags = request.POST.getlist('tagName')
form = forms.PostForm(request.POST, request.FILES)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.category = cat
if subcat:
post.sub_categories = subcat
if subfil:
post.filter_option = subfil
post.save()
add_tags = models.Tags.objects.filter(tag__in=tags)
post.tag.add(*add_tags) # new
return HttpResponseRedirect(reverse('blog_app:index'))
A Django form can handle ManyToManyFields itself, but can only do that with .save_m2m() or .save() without using commit=False: first it needs to save the Post object since it needs the primary key of that Post object to link the object to other items.
If your PostForm uses the tag field:
class PostForm(forms.ModelForm):
…
class Meta:
model = models.Post
# ↓ tag field
fields = ['tag', 'other', 'fields']
then we can let the form do the work for us:
from django.shortcuts import redirect
def adminNewPostView(request):
form = forms.PostForm()
if request.method == 'POST':
…
tags = request.POST.getlist('tagName')
form = forms.PostForm(request.POST, request.FILES)
if form.is_valid():
if subcat:
form.instance.sub_categories = subcat
if subfil:
form.instance.filter_option = subfil
form.instance.author = request.user
form.instance.category = cat
form.save()
return redirect('blog_app:index')
Related
The issue is next: I'm using existing templates for my site. I have an order page. I want to use my own template for form
I know that I can implement it just using {% for field in form %}. But I need to show not all fields. For example, here is my Order model:
class Order(models.Model):
state_choices = ('ACTIVE', 'COMPLETED', 'FROZEN')
order_date = models.DateTimeField(auto_now_add=True)
delivery_time = models.CharField(max_length=100)
address_city = models.CharField(max_length=40)
address_street = models.CharField(max_length=40)
address_building = models.CharField(max_length=40)
state = models.CharField(max_length=200, default='ACTIVE')
client = models.ForeignKey(CustomUser)
I need to show just: address_city, address_street, address_building and delivery_time. Because in view I just return current user and set it to client. Here is my view that saves Order:
def submit(request):
args = {}
args['form'] = OrderForm
if request.POST:
order_form = OrderForm(request.POST)
if order_form.is_valid():
order_form.save()
user = request.user
address_country = order_form.cleaned_data['address_country']
address_city = order_form.cleaned_data['address_city']
address_building = order_form.cleaned_data['address_building']
delivery_time = order_form.cleaned_data['delivery_time']
new_order = Order(address_country=address_country,
address_city=address_city,
address_building=address_building,
delivery_date=delivery_time, client=user)
new_order.save()
Basket.objects.filter(client=user).delete()
return redirect('/order/')
else:
return render(request, 'order_page.html', args)
I guess that new_order is odd, because order_form.save() saves it. But how to set user of current session to Order through form?
To understand it all, here is my OrderForm code:
class OrderForm(forms.ModelForm):
class Meta():
model = Order
fields = ['address_city', 'address_street', 'address_building', 'delivery_time']
I handle this kind of logic in the form's init method, and when creating the form in the view I pass in the request object as a kwarg for the form to get its data from, then hide the appropriate fields:
In forms.py...
class YourForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
request = kwargs.pop('request', None)
super(YourForm, self).__init__(*args, **kwargs)
if request:
self.fields['field'].initial = process_request_to_get_value(request)
self.fields['field'].widget = forms.HiddenInput()
# More logic here if needed
class Meta:
model = Order
In views.py...
def your_view(request):
if request.method == 'POST':
form = YourForm(request.POST, request = request)
if form.is_valid():
form.save()
return HttpResponseRedirect('somewhere')
else:
form = YourForm(request = request)
return render_to_response('template',
{'form': form,},
context_instance = RequestContext(request))
I haven't got much experience with building custom forms (only default model forms) and I'm looking for some help here. I'm trying to build a form that will get "feed_url" from user and if it already exist, just add reference into UserFeed model. In case it doesn't exist, it should add it to the Feed model and also reference it in UserFeed model as well.
models
class Category(models.Model):
name = models.CharField(unique=False, max_length=64)
user = models.ForeignKey(User)
slug = AutoSlugField(populate_from='name', always_update='True', unique_with='user')
def __unicode__(self):
return self.name
class Meta:
ordering = ('name',)
class Feed(models.Model):
feed_url = models.URLField(unique=True)
def __unicode__(self):
return self.feed_url
class UserFeed(models.Model):
feed = models.ForeignKey(Feed)
title = models.CharField(max_length=64)
category = models.ForeignKey(Category)
user = models.ForeignKey(User)
slug = AutoSlugField(populate_from='title', always_update='True', unique_with='user')
def __unicode__(self):
return self.title
class Meta:
ordering = ('title',)
forms
class UserFeedForm(forms.Form):
feed_url = forms.URLField()
title = forms.CharField(max_length=64)
category = forms.ModelChoiceField(Category)
user = forms.HiddenInput()
views
def addfeed(request):
categories = Category.objects.filter(user=request.user)
feeds = Feed.objects.all()
if request.method == 'POST':
form = UserFeedForm(request.POST)
form.fields['category'].queryset = categories
if form.is_valid():
feed = form.save(commit=False)
if form.fields['feed_url'] in feeds:
##### add to Feed object and add reference to UserFeed object
feed.user = request.user
feed.save()
else:
##### get id from Feed object and add reference to UserFeed object
feed.user = request.user
feed.save()
return HttpResponseRedirect("/reader/manage")
else:
form = UserFeedForm()
form.fields['category'].queryset = categories
context = {'form': form,}
return expand_context_and_render(request, context, 'reader/form.html')
Could anybody please point me in the right direction?
thanks
UPDATE
Now I have updated my view and form as follows:
forms
class UserFeedForm(forms.ModelForm):
feed = forms.URLField()
title = forms.CharField(max_length=64)
category = forms.ModelChoiceField(Category)
user = forms.HiddenInput()
class Meta:
model = UserFeed
fields = ['feed', 'title', 'category']
views
def addfeed(request):
categories = Category.objects.filter(user=request.user)
feeds = Feed.objects.all()
if request.method == 'POST':
form = UserFeedForm(request.POST)
form.fields['category'].queryset = categories
if form.is_valid():
feed = form.cleaned_data.get('feed')
if feed in feeds:
##### get id from Feed and add reference to UserFeed
existing_feed = Feed.objects.get(feed_url=feed)
form.feed = existing_feed.id
form.user = request.user
form.save(commit=True)
else:
##### add to Feed object and then add reference to UserFeed object
Feed.object.create(feed_url=feed)
existing_feed = Feed.objects.get(feed_url=feed)
form.feed = existing_feed.id
form.user = request.user
form.save(commit=True)
return HttpResponseRedirect("/reader/manage")
else:
form = UserFeedForm()
form.fields['category'].queryset = categories
context = {'page_title': page_title,
'form': form,
}
return expand_context_and_render(request, context, 'reader/form.html')
I think I'm getting closer but it still won't work, giving me this error:
"Cannot assign "u'http://feeds.bbci.co.uk/news/rss.xml'": "UserFeed.feed" must be a "Feed" instance."
Any idea how can I fix this?
thanks
Try taking a look at this question, and also inline formsets in the docs.
Additionally in your UserFieldForm, feed is defined as a URLField. So in your view, in this line:
feed = form.get_cleaned_data.get('feed')
your feed variable is simply a string, and not actually a Feed object. This is where your error might be occurring? That's where inline formsets could help you. Also, this line also makes your your if feed in feeds: statement moot, because feed is just a string, whereas feeds is a Queryset of all your Feed instances. I think this will cause your view to always end up in the else block?
I'm new to Django myself but I hope this helps you.
The logic you are looking for is:
Check if the feed exists.
If it exists, fetch it.
If it doesn't create a new feed.
Associate the feed with a UserFeed object.
The get_or_create method does parts 1-3, so you need the following:
if form.is_valid():
obj = form.save(commit=False)
feed = form.cleaned_data.get('feed')
feed_obj, created = Feed.objects.get_or_create(feed_url=feed)
obj.feed = feed_obj
obj.save()
return HttpResponseRedirect("/reader/manage")
I finally got it working like this:
forms
class UserFeedForm(forms.Form):
feed = forms.URLField()
title = forms.CharField(max_length=64)
category = forms.ModelChoiceField(Category)
user = forms.HiddenInput()
class Meta:
model = UserFeed
fields = ['feed', 'title', 'category']
views
def addfeed(request):
user = request.user
categories = Category.objects.filter(user=request.user)
if request.method == 'POST':
form = UserFeedForm(request.POST)
form.fields['category'].queryset = categories
if form.is_valid():
feed = form.cleaned_data['feed']
category = form.cleaned_data['category']
title = form.cleaned_data['title']
feed_obj, created = Feed.objects.get_or_create(feed_url=feed)
obj = UserFeed(feed=feed_obj, title=title, category=category, user=user)
obj.save()
return HttpResponseRedirect("/reader/manage")
else:
form = UserFeedForm()
form.fields['category'].queryset = categories
context = {'page_title': page_title,
'form': form,
}
return expand_context_and_render(request, context, 'reader/form.html')
Both tips from the answers below pointed me in the right direction. Thanks !
I'm trying to limit number of "categories" that user have available when entering new "feed" only to categories that he owns and he created. The way it works now is that user can add "feed" to other users' "categories" as this is what the form displays. How can I fix it ?
thanks!
-M
models.py
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=50)
user = models.ForeignKey(User)
class Feed(models.Model):
url = models.URLField()
name = models.CharField(max_length=50)
created = models.DateTimeField(auto_now_add=True)
description = models.TextField(blank=True)
category = models.ForeignKey(Category)
user = models.ForeignKey(User)
forms.py
class FeedForm(forms.ModelForm):
class Meta:
model = Feed
exclude = ['user']
views.py
def addfeed(request, user):
user = request.user
page_title = "Add feed"
instance = Category.objects.filter(user=request.user)
if request.method == 'POST':
form = FeedForm(request.POST, instance=instance)
if form.is_valid():
feed = form.save(commit=False)
feed.user = request.user
feed.save()
return HttpResponseRedirect("/user/" + user.username + "/manage")
else:
form = FeedForm()
return render(request, "form_manage.html", {
'page_title': page_title,
'form': form,
})
Set the queryset attribute of the field somewhere. Because it depends on your user, it's something you have to set during or after instantiating the form. For instance, here's how to do it in the view:
def addfeed(request, user):
user = request.user # why does this view take user as an arg and then reassign?
page_title = "Add feed"
categories = Category.objects.filter(user=request.user)
if request.method == 'POST':
form = FeedForm(request.POST)
form.fields['category'].queryset = categories
if form.is_valid():
feed = form.save(commit=False)
feed.user = request.user
feed.save()
return HttpResponseRedirect("/user/" + user.username + "/manage")
else:
form = FeedForm()
form.fields['category'].queryset = categories
return render(request, "form_manage.html", {
'page_title': page_title,
'form': form,})
I removed the instance argument to your POST case's form construction because that's meant for passing in an existing Feed instance, not a categories queryset.
You could also do this in the form's __init__ if you pass in the correct categories queryset.
I use javascript to do this. For example, you could pass a list of the relevant categories as extra context in your view then use javascript in your template to empty the pre-populated option field in the form and replace it with your extra context.
I'm trying to save a ModelForm, but I'm getting an error:
InternalError at /submeter/anuncio/
current transaction is aborted, commands ignored until end of transaction block
I will try to explain what I'm doing.
Ok, I have 5 Models that I need to save at once in one form. I'm testing to save on two models but as I said, I'm getting error on the save method. As a beginner I'm getting a little bit lost on the best way to achieve the task.
What I've done:
Template:
<form method="post" action="/submeter/anuncio/">{% csrf_token %}
{{form_main}}
{{form_bicyclead}}
<input type="hidden" name="section" value="5" />
<input type="submit">
</form>
Views.py:
def submit_data_entry_view(request):
if request.method == 'GET': #first time rendering the form
form_main = MainForm()
form_bicyclead = BicycleAdForm()
return render_to_response('app_t/submit_data_entry.html', {'form_main': form_main, 'form_bicyclead': form_bicyclead}, context_instance=RequestContext(request))
def submit_ad_view(request):
if request.method == 'POST':
post_values = request.POST.copy()
post_values['user'] = request.user.username
post_values['bicycleadtype'] = 2
post_values['bicycleaditemkind'] = 4
post_values['bicycleadcondition'] = 2
post_values['city'] = 4803854
form_main = MainForm(post_values)
form_bicyclead = BicycleAdForm(post_values)
if form_main.is_valid() and form_bicyclead.is_valid():
form_main.save()
#form_bicyclead.save()
resultado = 'valid'
else:
resultado = 'n_valid'
pdb.set_trace()
return render_to_response('app_t/submit_ad.html', {'resultado': resultado}, context_instance=RequestContext(request))
Forms.py:
class MainForm(forms.ModelForm):
class Meta:
model = Main
exclude = ('user', 'section')
class BicycleAdForm(forms.ModelForm):
class Meta:
model = BicycleAd
exclude = ('main', 'bicycleadtype', 'bicycleaditemkind', 'bicycleadcondition', 'city')
Models.py:
class Main(models.Model):
section = models.ForeignKey(Section)
user = models.ForeignKey(User)
title = models.CharField(max_length=250)
date_inserted = models.DateTimeField(auto_now_add=True)
date_last_update = models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.title
class Meta:
ordering = ['date_inserted']
class BicycleAd(models.Model):
main = models.ForeignKey(Main)
bicycleadtype = models.ForeignKey(BicycleAdType)
bicycleaditemkind = models.ForeignKey(BicycleAdItemKind) # MPTT Model
bicycleadcondition = models.ForeignKey(BicycleAdCondition)
country = models.ForeignKey(GeonamesCountry)
city = models.ForeignKey(GeonamesLocal)
date_inserted = models.DateTimeField(auto_now_add=True)
date_last_update = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['date_inserted']
My question is: How can I "override" the form_main.save() in the views.py and tell them all the fields that I have in the model "section, user and title"? I think the error is because of the fields "section" and "user" that are not passing to the save method. How can I pass this values?
Another question: I'm doing this the right way or there are easier and simple ways to achieve what I'm trying to achieve?
Best Regards
My approach is to replace this code:
def submit_ad_view(request):
if request.method == 'POST':
post_values = request.POST.copy()
post_values['user'] = request.user.username
post_values['bicycleadtype'] = 2
post_values['bicycleaditemkind'] = 4
post_values['bicycleadcondition'] = 2
post_values['city'] = 4803854
form_main = MainForm(post_values)
by:
def submit_ad_view(request):
if request.method == 'POST':
model = Main() #if model exists get it!
#Notice, Main is the name of your model.
model.user = request.user.username
...
model.bicycleaditemkind = 4
...
form_main = MainForm(request.POST, instance = model )
You can learn more on Creating forms from models django doc.
Modify your model form as:
class MainForm(forms.ModelForm):
def __init__(self, *args, **kw):
self.user = kw.pop('user')
self.section = kw.pop('section')
super(MainForm).__init__(*args, **kw)
class Meta:
model = Main
exclude = ('user', 'section')
def save(self, *args, **kw):
instance = super(MainForm).save(commit=False)
instance.user = self.user
instance.section = self.section
instance.save()
return instance
Now you need to pass the user and section when you are creating an intance of the form in your view:
form_main = MainForm(request.POST or None, user=request.user, section=section)
Django will just go to the else condition.
here's the code:
models.py
class StakeholderProfile(models.Model):
types = models.ForeignKey(Stakeholder)
name = models.CharField(blank=False, max_length=50)
forms.py
class SPForm(forms.ModelForm):
class Meta:
model = StakeholderProfile
exclude = ('key_contact_person',)
views.py
def profile(request):
stakeholderprofile = StakeholderProfile.objects.all()
if request.method == 'POST':
form = SPForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/profile/')
else:
form = SPForm()
return render_to_response('profile.html',{'form':form,'sp':stakeholderprofile})
I really need your help sir/maam.
You are excluding a field that doesn't exist in StakeHolderProfile.
Also be sure you added method='POST' in your form tag.