Django, Foreign key not getting filled with session data - django

im trying to fill my foreignkey (employer) with the user that is logged in, but i have seen alot of way but they havent worked for me, does anyone know what im doing wrong? and how i can fix it?
View:
class JobCreate(CreateView):
model = Job
form = JobCreateForm()
form_class = JobCreateForm
context = {}
success_url = reverse_lazy('jobsview')
def POST(self,request):
if request.method == 'POST':
form = JobCreateForm(request.POST)
if form.is_valid():
job = form.save(commit=False)
job.employer = request.user
job.save()
context = {}
return render(request, 'jobs/jobs.html',context)
else:
context = {}
return render(request, 'jobs/job_form.html',context)
Model:
class Job(models.Model):
employer = models.ForeignKey(User, related_name='employer', on_delete=CASCADE,blank=True)
employees = models.ManyToManyField(User, related_name='employees2user',null=True,blank=True)
title = models.CharField(max_length=200,)
description = models.CharField(max_length=200,null=True,blank=True)
category_id = models.ManyToManyField(Category,blank=True)
skill_id = models.ManyToManyField(Skill,blank=True)
approved = models.BooleanField(default=False)
# img = models.ImageField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self): # Default value
return self.title
HTML:
{% extends "jobs/layout.html" %}
{% block content %}
<h3> Job</h3>
<div class="container">
<div class="jobform">
<form action="" method="POST">
{%csrf_token%}
{% for field in form %}
<label for="{{field.id_for_label}}">{{field.html_name}}</label>
{{field}}
{% endfor %}
<p>Ctrl in houden om meerder te selecteren</p>
<button type="submit" class="btn btn-dark btn-space">Submit</button>
</form>
</div>
</div>
{%endblock%}

In your views method, try replacing
job = form.save(commit=False)
job.employer = request.user
job.save()
with
self.object = form.save(commit=False)
self.object.employer = self.request.user
self.object.save()
Also, what is the purpose of context {} ?
Can you just put this inside a form_valid method directly? That's much cleaner.
def form_valid(self, form):
self.object = form.save(commit=False)
self.object.employer = self.request.user
self.object.save()
return super(JobCreate, self).form_valid(form)

Related

Inlineformset_factory saving parent without child and not displaying validation errors if child is none

I am having 2 issues, one if you submit and click back and then submit again it duplicates the instance in the database - in this case Household. In addition it is saving the parent 'Household' without the child 'Applicants' despite me setting min_num=1
can someone point me in the right direction to resolve this issue.
Many thanks in advance
class Application(models.Model):
name = models.CharField(max_length=100, blank=True, null=True)
application_no = models.CharField(max_length=100, unique=True, default=create_application_no)
created_date = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
class HouseHold(models.Model):
name = models.CharField(max_length=100)
application = models.ForeignKey(Application, on_delete=models.CASCADE)
no_of_dependents = models.PositiveIntegerField(default=0)
class Applicant(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
household = models.ForeignKey("HouseHold", on_delete=models.CASCADE)
forms.py
class ApplicationForm(ModelForm):
class Meta:
model = Application
fields = (
"name",
)
class ApplicantForm(ModelForm):
class Meta:
model = Applicant
fields = [
"household",
"first_name",
"last_name"
]
class HouseHoldForm(ModelForm):
class Meta:
model = HouseHold
fields = [
'name',
'application',
'no_of_dependents'
]
def __init__(self, application_id=None, *args, **kwargs):
super(HouseHoldForm, self).__init__(*args, **kwargs)
self.fields['name'].label = 'House Hold Name'
if application_id:
self.fields['application'].initial = application_id
self.fields['application'].widget = HiddenInput()
ApplicantFormset = inlineformset_factory(
HouseHold, Applicant, fields=('household', 'first_name', 'last_name'), can_delete=False, extra=1, validate_min=True, min_num=1)
views.py
class HouseHoldCreateView(LoginRequiredMixin, generic.CreateView):
model = models.HouseHold
template_name = "households/household_create.html"
form_class = HouseHoldForm
def get_parent_model(self):
application = self.kwargs.get('application_pk')
return application
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.request.POST:
context['application'] = models.HouseHold.objects.filter(application_id=self.kwargs['application_pk']).last()
context['house_hold_formset'] = ApplicantFormset(self.request.POST, instance=self.object)
else:
context['application'] = models.Application.objects.get(id=self.kwargs['application_pk'])
context['house_hold_formset'] = ApplicantFormset()
return context
def get_form_kwargs(self):
kwargs = super(HouseHoldCreateView, self).get_form_kwargs()
print(kwargs)
kwargs['application_id'] = self.kwargs.get('application_pk')
return kwargs
def form_valid(self, form):
context = self.get_context_data()
applicants = context['house_hold_formset']
with transaction.atomic():
self.object = form.save()
if applicants.is_valid():
applicants.instance = self.object
applicants.save()
return super(HouseHoldCreateView, self).form_valid(form)
def get_success_url(self):
if 'addMoreApplicants' in self.request.POST:
return reverse('service:household-create', kwargs={'application_pk': self.object.application.id})
return reverse('service:household-list', kwargs={'application_pk': self.object.application.id})
I had a similar problem, I solved it by adding the post() method to the view. The example is an UpdateView but the usage is the same.
(the indentation is not correct but that's what stackoverflow's editor let me do, imagine all methods are 4 spaces to the right)
class LearnerUpdateView(LearnerProfileMixin, UpdateView):
model = User
form_class = UserForm
formset_class = LearnerFormSet
template_name = "formset_edit_learner.html"
success_url = reverse_lazy('home')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
learner = User.objects.get(learner=self.request.user.learner)
formset = LearnerFormSet(instance=learner)
context["learner_formset"] = formset
return context
def get_object(self, queryset=None):
user = self.request.user
return user
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
user = User.objects.get(learner=self.get_object().learner)
formsets = LearnerFormSet(self.request.POST, request.FILES, instance=user)
if form.is_valid():
for fs in formsets:
if fs.is_valid():
# Messages test start
messages.success(request, "Profile updated successfully!")
# Messages test end
fs.save()
else:
messages.error(request, "It didn't save!")
return self.form_valid(form)
return self.form_invalid(form)
Keep in mind that to save the formset correctly you have to do some heavy lifting in the template as well. I'm referring to the hidden fields which can mess up the validation process. Here's the template corresponding to the view posted above:
<form action="" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ form|crispy }}
{{ learner_formset.management_form}}
{% for form in learner_formset %}
{% if forloop.first %}
{% comment %} This makes it so that it doesnt show the annoying DELETE checkbox {% endcomment %}
{% for field in form.visible_fields %}
{% if field.name != 'DELETE' %}
<label for="{{ field.name }}">{{ field.label|capfirst }}</label>
<div id="{{ field.name }}" class="form-group">
{{ field }}
{{ field.errors.as_ul }}
</div>
{% endif %}
{% endfor %}
{% endif %}
{% for field in form.visible_fields %}
{% if field.name == 'DELETE' %}
{{ field.as_hidden }}
{% else %}
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{% endif %}
{% endfor %}
{% endfor %}
<input class="btn btn-success" type="submit" value="Update"/>
Additional reading :
https://medium.com/#adandan01/django-inline-formsets-example-mybook-420cc4b6225d
Save formset in an UpdateView
Inspired by Beikini
I have solved it using the create View
class HouseHoldCreateView(LoginRequiredMixin, generic.CreateView):
model = HouseHold
template_name = "households/household_create3.html"
form_class = HouseHoldForm
def get_parent_model(self):
application = self.kwargs.get('application_pk')
return application
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.request.POST:
context['application'] = HouseHold.objects.filter(
application_id=self.kwargs['application_pk']).last()
context['house_hold_formset'] = ApplicantFormset(self.request.POST)
else:
context['application'] = Application.objects.get(id=self.kwargs['application_pk'])
context['house_hold_formset'] = ApplicantFormset()
return context
def get_form_kwargs(self):
kwargs = super(HouseHoldCreateView, self).get_form_kwargs()
kwargs['application_id'] = self.kwargs.get('application_pk')
return kwargs
def form_valid(self, form):
context = self.get_context_data()
applicants = context['house_hold_formset']
application_id = self.kwargs['application_pk']
household_form = self.get_form()
if form.is_valid() and applicants.is_valid():
with transaction.atomic():
self.object = form.save()
applicants.instance = self.object
applicants.save()
messages.success(self.request, 'Applicant saved successfully')
return super(HouseHoldCreateView, self).form_valid(form)
else:
messages.error(self.request, 'please add an applicant to the household')
return self.form_invalid(form)
def get_success_url(self):
return reverse('service:household-list', kwargs={'application_pk': self.object.application.id})

update formset with class based view

i've created web blog with django 2.2 each post has multiple images , but when i try to update the post
images wont updated
i use class based view
class Post(models.Model):
user= models.ForeignKey(Account,on_delete=models.CASCADE)
title= models.CharField(max_length=100)
#others
class PostImage(models.Model):
post= models.ForeignKey(Post,on_delete=models.CASCADE,related_name='images')
media_files = models.FileField(upload_to=random_url)
and this my forms.py
class PostImageForm(forms.ModelForm):
class Meta:
model = PostImage
fields = [
'media_files'
]
class PostUpdateForm(forms.ModelForm):
class Meta:
model = Post
fields = [
'title','description',#and others
]
my views.py
PostImageFormSet = inlineformset_factory(
Post,PostImage,form=PostImageForm,extra=1,can_delete=True,can_order=False
)
class PostUpdateView(LoginRequiredMixin,UserPassesTestMixin,UpdateView):
model = Post
form_class = PostUpdateForm
template_name = 'posts/update_post.html'
def get_context_data(self,**kwargs):
data = super().get_context_data(**kwargs)
if self.request.POST:
data['images'] = PostImageFormSet(self.request.POST or None,self.request.FILES,instance=self.object)
else:
data['images'] = PostImageFormSet(instance=self.object)
return data
def form_valid(self,form):
context = self.get_context_data()
images = context['images']
with transaction.atomic():
if form.is_valid() and images.is_valid():
self.object = form.save()
images.instance = self.object
images.save()
return super().form_valid(form)
def test_func(self):
post = self.get_object()
if self.request.user.username == post.user.username:
return True
return False
def get_success_url(self):
return reverse_lazy('post:post-detail',kwargs={'slug':self.object.slug})
my templates
<form enctype="multipart/form-data" method="post" action="">
{% csrf_token %}
{{images.management_form }}
{{ form|crispy }}
{% for img in images %}
<label>{{img.media_files.label}}</label>
{{img.media_files}}
{% endfor %}
<button type="submit" class="btn btn-block btn-primary">update</button>
</form>
i'm wondering why didnt update the posts image !?
thanks for replay ..

Django - Modelform not rendering

I created a form to update a User's profile, however when I run it, there are no errors, but when I try to open up the page, the header appears but the UpdateBioForm does not appear. Secondly, I was wondering how you would create a large textbox to store someone's biography.
Models.py
class UserProfile(models.Model):
user = models.OneToOneField(User)
biography = models.CharField(max_length = 255, default = '')
city = models.CharField(max_length=100, default = '')
website = models.URLField(default='')
image = models.ImageField(upload_to='profile_image', blank=True)
def setdefault(self, default_path='/profile_image/Default.jpg'):
if self.image:
return self.image
return default_path
def __str__(self):
return self.user.username
Forms.Py
class UpdateBioForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = (
'biography',
'city',
'website'
)
def save(self, commit=True):
savedBio = super(UpdateBioForm, self).save(commit=False)
savedBio.biography = self.cleaned_data['biography']
savedBio.city = self.cleaned_data['city']
savedBio.website = self.cleaned_data['website']
if commit:
savedBio.save()
return savedBio
Views.py
def update_bio(request):
if request.method == 'POST':
form = UpdateBioForm(request.POST, instance=request.user)
if form.is_valid():
form.save()
return redirect('/')
else:
form = UpdateBioForm(instance=request.user)
args = {'form':form}
return render(request, 'accounts/update_bio.html')
urls.py
url(r'^profile/updatebio/$',views.update_bio, name='update_bio'),
update_bio.html
{% extends 'base.html' %}
{% block body %}
<div class="container">
<h1>Update Biography</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
</div>
{% endblock %}
You are not passing any context to your render() method - you define args but don't do anything with that variable. Change it to:
args = {'form':form}
return render(request, 'accounts/update_bio.html', context=args) # <-- You're missing context

Radio-buttons are not displayed in the Class Based Views, its the check-boxes showing

Radio-buttons are not showing, its the check-boxes displayed in the Class Based Views. I want them to be showing the radio-buttons
forms.py
class ProductImagesForm(forms.ModelForm):
media = forms.ImageField(label='Image')
def __init__ (self, *args, **kwargs):
super(PerstransForm, self).__init__(*args, **kwargs)
self.fields['featured_image'] = forms.BooleanField( widget = forms.RadioSelect(choices=((self.prefix, 'featured'),))
def add_prefix(self, field):
if field == 'featured_image': return field
else: return self.prefix and ('%s-%s' % (self.prefix, field)) or field
class Meta:
model = ProductImages
fields = ['media', 'featured_image']
ImagesFormset = modelformset_factory(ProductImages, fields=('media', 'featured_image'), extra=1)
models.py
def product_download(instance, filename):
return '%s/%s' %(instance.product.slug, filename)
class ProductImages(models.Model):
product = models.ForeignKey(Product)
title = models.CharField(max_length=120)
media = models.ImageField(upload_to=product_download,
width_field='max_width',
height_field='max_height',
null=True, blank=True)
max_width = models.CharField(max_length=100, null=True, blank=True)
max_height = models.CharField(max_length=100, null=True, blank=True)
featured_image = models.BooleanField(default=True)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
def __unicode__(self):
return unicode(self.media)
class Meta:
verbose_name = "product image"
verbose_name_plural = "product images"
Views.py
def get(self, *args, **kwargs):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
formset = ImagesFormset(queryset=ProductImages.objects.none())
return self.render_to_response(self.get_context_data(form=form, formset=formset))
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form_class = self.get_form_class()
form = self.get_form(form_class)
formset = ImagesFormset(self.request.POST, self.request.FILES)
form_valid = form.is_valid()
formset_valid = formset.is_valid()
if form_valid and formset_valid:
seller = self.get_account()
form.instance.seller = seller
self.object = form.save()
media = formset.save(commit=False)
for img in media:
img.product = self.object
img.save()
formset.save()
return self.form_valid(form, formset)
else:
return self.form_invalid(form, formset)`
template
<form enctype="multipart/form-data" action="" method="post"> {% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{{ form.as_p }}
{{ formset.management_form }}
<div class="link-formset">
{{ formset }}
{% for choice in formset.featured_image %}
<ul>
<li><input type="radio" name="featured"></li>
</ul>
{% endfor %}
</div>
<input type="submit" value="{{ submit_btn }}">
</form>
Hope someone can help me to fix in the missing pieces
This will happen because you choose BooleanField that means a specific selection is either 0 or 1 and radio button is used to select one option from multiple choices hence you should try using forms.ChoiceField instead of forms.BooleanField.
for more details read this official django doc
I have got to some point and I guess it will help some folks
template
{{ formset.management_form }}
<div class="link-formset">
{% for choice in formset %}
<div>
{{ choice.media }}
<input type="radio" name="{{choice.featured_image.label}}">{{ choice.featured_image.label }}</
</div>
{% endfor %}
</div>

How to partially update an object when using modelForm in Django?

I want to let users to add/update caption of their already updated photos without changing any other field of the photo.
Here is the model:
class UserPic(models.Model):
user = models.ForeignKey(User, unique=False)
picfile = ImageWithThumbsField(upload_to= get_uplaod_file_name, sizes=((648,648),(200,200),(1200,1200)))
caption = models.CharField(max_length=200 , blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
likes = models.IntegerField(default=0)
liked_by= models.ForeignKey(Liker, blank=True)
#models.permalink
def get_absolute_url(self):
return ('view_pirate', None, {'user': self.account.user})
def __unicode__(self):
return unicode(self.picfile.name)
views.py
def edit_photo(request, pic_id):
pic = UserPic.objects.get(id=pic_id)
if request.method == 'POST':
if pic.user== request.user:
picform = CaptionForm(request.POST)
if picform.is_valid():
edform = picform.save(commit=False)
edform.caption = request.POST['caption']
edform.save()
message = "caption is uploaded"
else:
edform = CaptionForm()
args = {}
args.update(csrf(request))
args['pic'] = pic
args['pic_id'] = pic_id
#args['form'] = edform
return render_to_response('userpics/photo.html', args,
context_instance= RequestContext(request))
photo.html
<div class="caption">
<form action="{% url "userpics.views.edit_photo" pic.id %}" method="POST">
{% csrf_token %}
{{form.as_ul}}
<input type="submit" value="SEND">
</form>
forms.py:
class CaptionForm(forms.ModelForm):
class Meta:
model= UserPic
fields = ('caption',)
However when I post the form, django still complain that:
MultiValueDictKeyError at /pics/edit/26
"'caption'"
I really got confused as I could not find any resources to deal with this particular problem. So appreciate your hints.
Ok I managed to solved the problem like this:
photo.html
<form action="{% url "userpics.views.edit_photo" pic.id %}" method="POST">
{% csrf_token %}
<input type="text" name="caption" value="{{pic.caption}}">
<input type="submit" value="SEND">
</form>
views.py
def edit_photo(request, pic_id):
pic = UserPic.objects.get(id=pic_id)
if request.method == 'POST':
if pic.user== request.user:
picform = CaptionForm(request.POST)
if picform.is_valid():
pic.caption = request.POST['caption']
pic.save()
message = "caption is uploaded"
else:
edform = CaptionForm(instance=pic)
args = {}
args.update(csrf(request))
args['pic'] = pic
args['pic_id'] = pic_id
return render_to_response('userpics/photo.html', args,
context_instance= RequestContext(request))