ManyToMany does not save to db Django - django

I have a ManytoMany field and the results of the CheckboxSelectMultiple don't get saved in the db and i don't understand why. It must be really simple but...
Here's the code:
models.py
class Person(models.Model):
last_name = models.CharField(max_length = 50)
first_name = models.CharField(max_length = 50)
def __str__(self):
return self.last_name +" "+self.first_name
class Event(models.Model):
owner = models.ForeignKey(User, on_delete=models.SET_NULL, null = True)
person = models.ManyToManyField(Person)
forms.py
class EventForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.get('user',None)
#self.user = kwargs.pop('user',None)
super(EventForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_method = 'POST'
#self.helper.form_action = reverse_lazy('simpleuser')
self.helper.add_input(Submit('submit', 'Submit', css_class='btn-success'))
class Meta:
model = Event
fields = ['person']
widgets ={
'person': forms.CheckboxSelectMultiple,
}
views.py
def uploadevent(request):
if request.method == "POST":
form =EventForm(request.POST)
if form.is_valid():
event = form.save(commit=False)
event.owner = request.user
event.save()
else:
form = EventForm()
return render...

See the response on this stackoverflow question: Saving Many To Many data via a modelform in Django
Quoting the OP:
When using commit=False, you have to call save_m2m()
m2m relationships require the parent object to be saved first, which
you are not doing by using commit=False
Just add this line below event.save()
if form.is_valid():
event = form.save(commit=False)
event.owner = request.user
event.save()
form.save_m2m()
Ref: https://docs.djangoproject.com/en/2.0/topics/forms/modelforms/#the-save-method

To save manytomany you should call save_m2m() when using commit=False (check details here):
if form.is_valid():
event = form.save(commit=False)
event.owner = request.user
event.save()
form.save_m2m()

Related

Django - UpdateView with multi model forms - one submit

I'm developing the web site where users can register and edit their information. I would like to use multi form in the single page. I can develop register form using CreateView. But when I use UpdateVIew, I don't know how to add initial value in multi form.
My code(view.py) is below. I can add form data as initial value, but not form2. Please tell me how to add form2 data as initial value.
▪️view.py
class UserUpdate(OnlyYouMixin, generic.UpdateView):
model = Profile
form_class = ProfileForm
second_form_class = ProfileNearStationForm
template_name = 'accounts/user_form.html'
def get_context_data(self, **kwargs):
context = super(UserUpdate, self).get_context_data(**kwargs)
if 'form' not in context:
context['form'] = self.form_class(self.request.GET, instance=self.request.user)
if 'form2' not in context:
context['form2'] = self.second_form_class(self.request.GET, instance=self.request.user)
return context
def get(self, request, *args, **kwargs):
super(UserUpdate, self).get(request, *args, **kwargs)
form = self.form_class
form2 = self.second_form_class
return self.render_to_response(self.get_context_data())
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.form_class(request.POST, instance=self.request.user)
form2 = self.second_form_class(request.POST, instance=self.request.user)
if form.is_valid() and form2.is_valid():
profile = form.save(commit=False)
profile.save()
station = form2.save(commit=False)
station.user = user
station.save()
messages.success(self.request, 'Settings saved successfully')
return HttpResponseRedirect(self.get_success_url())
else:
return self.render_to_response(
self.get_context_data(form=form, form2=form2))
def get_success_url(self):
return resolve_url('accounts:user_form', pk=self.kwargs['pk'])
▪️form.py
class ProfileForm(forms.ModelForm):
name = forms.CharField(required=True)
tel_number = forms.CharField(required=True)
class Meta:
model = Profile
fields = ('name', 'tel_number')
class ProfileNearStationForm(forms.ModelForm):
prefecture = forms.ModelChoiceField(queryset=areas_model.Prefecture.objects.all(), required=True)
railway = forms.ModelChoiceField(queryset=areas_model.Railway.objects.none(), required=True)
station = forms.ModelChoiceField(queryset=areas_model.Station.objects.none(), required=True)
memo = forms.CharField(required=False)
class Meta:
model = ProfileNearStation
fields = ('prefecture', 'railway', 'station', 'memo')
▪️model.py
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
name = models.CharField(max_length=255)
tel_number = models.CharField(max_length=13)
class ProfileNearStation(models.Model):
prefecture = models.ForeignKey(areas_model.Prefecture, on_delete=models.CASCADE)
railway = models.ForeignKey(areas_model.Railway, on_delete=models.CASCADE)
station = models.ForeignKey(areas_model.Station, on_delete=models.CASCADE)
memo = models.CharField(max_length=255, null=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)

I can not save an object related to the User object

I'm trying to add an instance to the Books model, which is added using the inheriting form of ModelForm, the Books instance is added, but I'm not able to relate the user field to the User model.
forms.py
from django import forms
from .models import Books
class BooksCreationForm(forms.ModelForm):
class Meta:
model = Books
fields = ('title',
'language', 'status', 'short_desc',
)
models.py
User = settings.AUTH_USER_MODEL
class Books(models.Model):
STATUS_CHOICES = (
('Reading', 'Reading'),
('Finished', 'finished'),
)
user = models.ForeignKey(User, on_delete=models.SET_NULL, related_name='books',null=True)
title = models.CharField(max_length=120)
short_desc = models.TextField(max_length=1000)
slug = AutoSlugField(populate_from='title', overwrite=True)
language = models.CharField(max_length=20, blank=True)
status = models.CharField(max_length=35, choices=STATUS_CHOICES)
views.py
class AddBooksView(View):
# ....
def post(self, request, *args, **kwargs):
template = self.template
form = self.form(request.POST)
# ?? user = User.objects.get(request.user)
user = User.objects.get(username=request.user.username)
if form.is_valid():
form.save(commit=False)
form.user = user
form.save()
print("book post sucess")
return redirect('main:books')
context = {'form': self.form()}
return render(request, template, context)
But when i add in the shell, works:
>>> u = User.objects.get(username='marcos')
>>> u.email
'marcos#marcos.com'
>>> bk = Books.objects.create(title='stackoverflow', language='Python', status='reading', short_desc='desc...')
>>> bk.title
'stackoverflow'
>>> bk.user = u
>>> bk.user.username
'marcos'
>>> bk.save()
>>> for v in Books.objects.filter(user=u):
... print(v.title, v.user.username)
...
stackoverflow marcos
several instances that i try save through ModelForm have user = None:
>>> for v in Books.objects.all():
... print(v.user, v.title)
...
None Pyhon2
None dfdsffffff
None fgdfdfd
None fgdfgfg
marcos stackoverflow # that i add before using shell
I also try removing user in the view and using this in the form:
class BooksCreationForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
return super(BooksCreationForm, self).__init__(*args, **kwargs)
def save(self, *args, **kwargs):
kwargs['commit'] = False
instance = super(BooksCreationForm, self).save(*args, **kwargs)
if self.request:
instance.user = self.request.user
instance.save()
return instance
class Meta:
model = Books
fields = ('title',
'language', 'status', 'short_desc',
)
You have to get the new book instance and assign the user to it, not to the form try:
if form.is_valid():
book = form.save(commit=False)
book.user = user
book.save()
return redirect('main:books')
See examples here Form's save method.
Try using CreateView for your AddbooksView as:
class AddBooksView(CreateView):
# ...
#specify your form
def form_valid(self, form):
form.save(commit=False)
form.user = self.request.user
form.save()
print("book post sucess")
return redirect('main:books')
#...
For reference follow this link: http://melardev.com/eng/blog/2017/11/04/createview-django-4-examples/

Django model : save object with custom field

I would like to know how I can do that : save an object in my database through Django model with custom field.
This is my modelclass :
class Document(EdqmTable):
language = models.CharField(max_length=2, verbose_name=_('language'), choices=LANGUAGE_CHOICES, null=False)
format = models.CharField(max_length=10, verbose_name=_('format'), choices=FORMAT_CHOICES, null=False)
title = models.CharField(max_length=512, verbose_name=_('document title'), null=False)
publication = models.ForeignKey(Publication, verbose_name=_('publication title'), null=False,
related_name='documents')
class Meta:
verbose_name = _('document')
verbose_name_plural = _('documents')
def save(self, *args, **kwargs):
self.title = f"{self.publication.pub_id}-{self.format.upper()}"
super(Document, self).save(*args, **kwargs)
I have a save method, which let to define my field title with combination between two fields.
title field is hidden in my django form and is set automatically by Django.
But, up to now, it doesn't work because my object is not saved into my database. Something is wrong in my save function ?
EDIT :
I edited my post with forms.py file and my view :
The field document.title is used in my django forms with formset like this :
class PublicationForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['category'].empty_label = _('Select a category') # Modify initial empty_label
class Meta:
model = Publication
fields = ['title', 'pub_id', 'category']
class DocumentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(DocumentForm, self).__init__(*args, **kwargs)
self.fields['title'].widget = forms.HiddenInput()
for key in self.fields:
self.fields[key].required = True
class Meta:
model = Document
fields = ['publication', 'language', 'format', 'title', 'upload']
DocumentFormSet = inlineformset_factory(Publication, Document, form=DocumentForm, extra=1)
And my view according to this part :
class PublicationCreateView(EdqmCreateView):
""" Create publication with document form through formset """
model = Publication
template_name = 'freepub/publication_form.html'
def get_context_data(self, **kwargs):
context = super(PublicationCreateView, self).get_context_data(**kwargs)
context['document_form'] = DocumentFormSet(self.request.POST or None, self.request.FILES or None)
return context
def form_valid(self, form):
context = self.get_context_data()
document = context['document_form']
if document.is_valid():
self.object = form.save()
document.instance = self.object
document.save()
return super(PublicationCreateView, self).form_valid(form)
def get_success_url(self):
return reverse('publication-list-crud')

django: ForeignKeyField limit_choices_to "parents pk"?

I've a model (Parent model):
class Post(models.Model):
image = models.ImageField(upload_to='%Y/%m/%d')
title = models.CharField(max_length=200)
width = models.DecimalField(max_digits=3, decimal_places=0)
height = models.DecimalField(max_digits=3, decimal_places=0)
year = models.PositiveIntegerField()
def __str__(self):
return self.title
and another model (Child model):
class Addimg(models.Model):
post = models.ForeignKey('Post', null=True)
addimg = models.ImageField(upload_to='%Y/%m/%d')
def __str__(self):
return self.post
My Addimg Form:
class AddimgForm(forms.ModelForm):
class Meta:
model = Addimg
fields = ('post', 'addimg', 'width', 'height',)
views.py using the form:
def addimg(request, pk):
if request.method == "POST":
form = AddimgForm(request.POST, request.FILES)
post = get_object_or_404(Post, pk=pk)
if form.is_valid():
addimg = form.save(commit=False)
addimg.addimg = request.FILES['addimg']
addimg.save()
return redirect('blog.views.detail', pk=post.pk)
else:
form = AddimgForm()
return render(request, 'blog/edit.html', {'form': form})
And my Problem is that when I create a "Child model" my post field returns all instances of allready created Post models as choices. What I want is that it automatic only displays the one Post it is related to without choices. Is ForeignKey the right model for that?
Any Ideas how this could work. thanks
ForeignKey field is translated into ModelChoiceField inside a Django ModelForm. If you inspect that class you will notice that this type of field has an queryset attribute required. By default Django provides the full set of objects. You can override this inside your form __init__ method by providing the parent object the form will need.
Consider the following example code:
def addimg(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = AddimgForm(request.POST, request.FILES, post=post)
#...
else:
form = AddimgForm(post=post)
return render(request, 'blog/edit.html', {'form': form})
class AddimgForm(forms.ModelForm):
class Meta:
model = Addimg
fields = ('post', 'addimg', 'width', 'height',)
def __init__(self, *args, **kwargs):
post = kwargs.pop('post')
super(AddimgForm, self ).__init__(*args, **kwargs)
self.fields['post'].queryset = Post.objects.filter(id=post.id)
What you want to do is create a Many-to-one relationship. For example,
post = models.ForeignKey('Post', null=True)
This means you can filter on it for example,
Addimg.objects.filter(post=Post)
or
Post.objects.get(pk=1)
Post.addimg_set.filter(xyz=etc)
Read more here: https://docs.djangoproject.com/en/1.8/topics/db/examples/many_to_one/

Setting user_id when saving in view - Django

I need to set the user that is creating the post in the add view:
#login_required
def add(request):
if request.method == 'POST':
form = BusinessForm(request.POST)
form.user_id = request.user.id
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('listing.views.detail'), args=(f.id))
else:
form = BusinessForm()
return render_to_response('business/add.html', {'form':form},
context_instance=RequestContext(request))
I assign the user id form.user_id = request.user.id but when saving, it still gives me an error Column user_id cannot be null
Am I doing something wrong? Thanks
EDIT:
I am excluding the user from the form in the model:
class BusinessForm(ModelForm):
class Meta:
model = Business
exclude = ('user',)
Could that be causing the problem?? How can I work around this?
EDIT 2:
Edited my BusinessForm() class as suggested but did not work:
class BusinessForm(ModelForm):
class Meta:
model = Business
exclude = ('user',)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
return super(BusinessForm, self).__init__(*args, **kwargs)
def save(self, *args, **kwargs):
kwargs['commit']=False
obj = super(BusinessForm, self).save(*args, **kwargs)
if self.request:
obj.user = self.request.user
obj.save()
Business model
class Business(models.Model):
name = models.CharField(max_length=200)
user = models.ForeignKey(User, unique=False)
description = models.TextField()
category = models.ForeignKey(Category)
address = models.CharField(max_length=200)
phone_number = models.CharField(max_length=10)
website = models.URLField()
image = models.ImageField(upload_to='business_pictures',blank=True)
You don't have to use init or save overrides for this.
You're just setting an attribute on your form and the form doesn't do anything with it. It doesn't magically behave like a model instance (your form wouldn't have a user_id attribute).
Since your form is a ModelForm, you can simply call save on it with commit=False to get the unsaved instance, set the user, then call save on the instance.
if request.method == 'POST':
form = BusinessForm(request.POST)
if form.is_valid():
business = form.save(commit=False)
business.user = request.user
business.save()
this seems to be exactly what your looking for.