Combine DetailView and UpdateView? - django

I am new to Django and I need to know how to have DetailView and UpdateView on the same Page.
I have two Models:
class Company(models.Model):
CustomerNo = models.AutoField(primary_key=True)
Company = models.CharField(max_length=200)
Str = models.CharField(max_length=200)
Zip = models.IntegerField()
City = models.CharField(max_length=200)
Name = models.CharField(max_length=200)
Phone = models.IntegerField()
Mobile = models.IntegerField()
Email = models.EmailField(max_length=200)
Web = models.CharField(max_length=200)
Info = models.CharField(max_length=200)
def __str__(self):
return self.Company
class Contact(models.Model):
Contact_Company = models.ForeignKey(Company, on_delete=models.CASCADE)
Contact_Name = models.CharField(max_length=200)
Contact_Phone = models.IntegerField()
Contact_Mobile = models.IntegerField()
Contact_Fax = models.IntegerField()
Contact_E_Mail = models.EmailField()
Contact_Web = models.CharField(max_length=200)
def __str__(self):
return self.Contact_Name
I want to build a page where I can see the company data from the first model and an update form for contacts realeted to the first model.
I enter the page with pk, from the previous page, its a DetailView for the first Model and with additionally context to list the Contact data with a for loop in Template.
I can use UpdateView to get data in the form and save it. but I don't know
how do display the realeted Company on the same page. Is there a way to use DetailView and UpdateView together?
I can use this UpdateView to change the Contact data, but I don't know how to include extra context from the first model to display the address on same page.
The success URL is wrong too.
I need to pass the pk from the first model so I can go back to the right list on previous page.
class ContactUpdate(UpdateView):
model = Contact
form_class = ContactCreateForm
template_name = 'customer/contact_update.html'
def get_success_url(self):
return reverse('customer_list', kwargs={'pk': self.object.pk})
def get_context_data(self, **kwargs):
context = super(ContactUpdate, self).get_context_data(**kwargs)
return context
Maybe FormMixin is a solution, I used it to Display the Data from
first Model and form for second Model on same Page. But I am really stuck
to realize this with UpdateForm.
I hope you understand my problem, sorry for my english.
Thank you for your help.
Forms.py
from django.forms import ModelForm
from .models import Company
from .models import Contact
from django.forms import HiddenInput
from django import forms
class CompanyCreateForm(ModelForm):
class Meta:
model = Company
fields = '__all__'
class ContactCreateForm(ModelForm):
class Meta:
model = Contact
widgets = {'Contact_Company': forms.HiddenInput()}
fields = [
'Contact_Company',
'Contact_Name',
'Contact_Phone',
'Contact_Mobile',
'Contact_Fax',
'Contact_E_Mail',
'Contact_Web',

You need to add form in the detail view,
class PostDetailView(DetailView):
model = Post #your model name
template_name = 'detail.html' #your template
# here you will add your form
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context['contactupdateform'] = ContactCreateForm()
return context
# Add POST method
def post(self, request, slug):
post = get_object_or_404(Post, slug=slug)
form = contactupdateform(request.POST)
if form.is_valid():
# from here you need to change your post request according to your requirement, this is just a demo
obj = form.save(commit=False)
obj.post = post
obj.author = self.request.user #to add the user
obj.save()
return redirect('detail', post.slug) #add your url
Make sure you are adding the POST request correctly, according to your model and url. This is an outline you can refer.
To add the form in the HTML, you need to do this,
{% for form in contactupdateform %}
<-- Add Your CSRF token and form here --!>
{% endfor %}

You can import this (LoginRequiredMixin) and insert in the updateview as an argument as the contact is a user
then try putting this in the models.py file :
def get_absolute_url(self):
return reverse('customer_list', kwargs={'pk': self.pk})
and remove (get_success_url) from views.py
You might need these too in the updateview Class "ContactUpdate"
login_url = '/login/'
redirect_field_name = <-- template path(html) of the page you want to reverse to...
HOPE THIS HELPS...

Related

Django Formset: how to get the current user? (using django-extra-views)

I'm used to collecting the current logged in user in a CreateView and passing it to the form like so:
class MakeFantasyTeam(CreateView):
form_class = MakeFantasyTeamForm
[...]
def form_valid(self, form):
form.instance.team_manager = self.request.user
form.save()
return super(MakeFantasyTeam, self).form_valid(form)
However, this doesn't seem to work when using an InlineFormSetView as provided by django-extra-views. I get an error NOT NULL constraint failed: tournament_invite.invited_by_id and I'm not sure how to get the user.id passed on to the form.
My View:
class InvitePlayersView(InlineFormSetView):
template_name = 'invite_players.html'
model = Tournament
inline_model = Invite
form_class = InvitePlayerForm
pk_url_kwarg = 'tourney_id'
factory_kwargs = {'can_delete': False, 'extra': 1}
def formset_valid(self, formset):
tourney_id = self.kwargs['tourney_id']
formset.instance.invited_for = Tournament.objects.filter(id=tourney_id).get()
formset.instance.invited_by = self.request.user
formset.save()
return super(InvitePlayersView, self).formset_valid(formset)
def get_success_url(self):
return reverse('make_team', kwargs={'tourney_id': self.object.invited_for.id})
My Model:
class Invite(models.Model):
name = models.CharField(max_length=200, blank=True, null=True)
email = models.CharField(max_length=320, null=False, blank=False, validators=[EmailValidator],)
invited_by = models.ForeignKey(get_user_model(), on_delete=models.DO_NOTHING)
invited_for = models.ForeignKey(Tournament, on_delete=models.DO_NOTHING)
created_dt = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.email
def get_absolute_url(self):
return reverse('home')
My Form:
class InvitePlayerForm(forms.ModelForm):
class Meta:
model = Invite
fields = ('name', 'email',)
Any tips or hints much appreciated!
Thank you,
Jon
Edit: Just to clarify what I'm trying to do here; I want a user to submit a formset. The data of that formset should be stored in the model, and the userid of the submitting user should also be stored in the model. I don't seem to be able to pass on the userid though.
I am not sure what you exactly want to do here, As per my understanding you want to use the currently logged in user's information. To do so you can append the user's info in the session dictionary. After that you can use the information in templates or in other views too.
In authentication view
def login(request):
#your necessary data
request.session['user_id']=The_user_id
request.session['user_name']=The_userName
To access data in the template
{% request.session.user_id %}
{% request.session.user_name %}
To access data in other views
def myview(request):
user_id= request.session['user_id']
user_name= request.session['user_name']

How to call django forms inlineformset into django templates

i am new to django and learning some from stackoverflow. Now i am creating a website for post with images and title. I found ways to connect my two models (images and post) at https://stackoverflow.com/a/62158885/13403211. it is working fine when i add post from admin. But i want to know how can i add those inlineformset fields into my template for user to fill in.Does anyone knows??
Here is the code i found. I copy the same code in my app to try.
models.py
class Item(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="items")
name = models.CharField(max_length=100)
class ItemImage(models.Model):
item = models.ForeignKey(Item, on_delete=models.CASCADE)
img = models.ImageField(default="store/default_noitem.jpg", upload_to=get_image_dir)
forms.py
from django import forms
from django.forms.models import inlineformset_factory
from .models import Item, ItemImage
class ItemImageForm(forms.ModelForm):
class Meta:
model = ItemImage
exclude = ()
class ItemForm(forms.ModelForm):
class Meta:
model = Item
fields = ["name",]
ItemImageFormSet = inlineformset_factory(
Item, ItemImage, form=ItemImageForm,
fields=['img'], extra=3, can_delete=True # <- place where you can enter the nr of img
)
views.py
class ItemCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
template_name = "items/add_item_form.html"
success_message = 'Item successfully added!'
form_class = ItemForm
def get_context_data(self, **kwargs):
data = super(ItemCreateView, self).get_context_data(**kwargs)
data['form_images'] = ItemImageFormSet()
if self.request.POST:
data['form_images'] = ItemImageFormSet(self.request.POST, self.request.FILES)
else:
data['form_images'] = ItemImageFormSet()
return data
def form_valid(self, form):
context = self.get_context_data()
form_img = context['form_images']
with transaction.atomic():
form.instance.user = self.request.user
self.object = form.save()
if form_img.is_valid():
form_img.instance = self.object
form_img.save()
return super(ItemCreateView, self).form_valid(form)
I have search on google and i did not find any related to this. Or am i doing wrong? Can someone help me?

Setting form fields in django class based generic view CreateView

I'm using django's CreateView to add images to a book. I pass the book's id to the class based view as a parameter in the url. Form fields such as book and language are not rendered on the template, rather they're obtained with the help of the book's id.
# views.py
class PictureCreateView(CreateView):
model = Upload
fields = "__all__"
book_id = None
def get_initial(self):
initial = super(PictureCreateView, self).get_initial()
initial = initial.copy()
self.book_id = self.kwargs['book_id']
book = Book.objects.get(id=self.book_id)
initial['book'] = book
initial['language'] = language
initial['uploader'] = self.request.user
return initial
# set book_id so it used in the template
def get_context_data(self, **kwargs):
context = super(PictureCreateView, self).get_context_data(**kwargs)
context['book_id'] = self.book_id
return context
def form_valid(self, form, **kwargs):
print('Form is valid')
self.object = form.save()
files = [serialize(self.object)]
data = {'files': files}
response = JSONResponse(data, mimetype=response_mimetype(self.request))
response['Content-Disposition'] = 'inline; filename=files.json'
return super(PictureCreateView, self).form_valid(form)
def form_invalid(self, form):
print('Form invalid!')
print(form.errors)
data = json.dumps(form.errors)
return HttpResponse(content=data, status=400, content_type='application/json')
# models.py
class Upload(models.Model):
image = models.ImageField(upload_to=get_upload_path, help_text='Image to process')
uploader = models.ForeignKey(settings.AUTH_USER_MODEL, models.CASCADE, related_name='uploader')
language = models.ForeignKey(Language, models.CASCADE)
book = models.ForeignKey(Book, models.CASCADE)
The problem is that I get an error saying the form is invalid, and the fields uploader, book and language are required. How do I resolve this?
The initial data is used to display the defaults when the form is initially displayed. It isn't used when those values are missing from the submitted form data. If fields like book and uploader are set from the URL or logged-in user, then you should leave them out of the form completely, instead of setting them in the initial data. You can then set the values on the instance in the form_valid method before the form is saved.
from django.contrib.auth.mixins import LoginRequiredMixin
class PictureCreateView(LoginRequiredMixin, CreateView):
model = Upload
fields = ['other_field1', 'other_field2', ...] # leave out book, language and uploader
def form_valid(self, form):
self.book_id = self.kwargs['book_id']
book = Book.objects.get(id=self.book_id)
form.instance.book = book
form.instance.language = ????
form.instance.uploader = self.request.user
return super(
The LoginRequiredMixin makes sure that only logged-in users can access the view.
You may want to use get_object_or_404 to handle the case where book_id refers to a book that does not exist.
One thought, initial doesn't fill the model for submission. You need to do that in init
def __init__(self):
super(PictureCreateView, self).__init__()
self.fields['book'] = self.initial['book']
self.fields['uploader'] = self.initial['uploader']
self.fields['language'] = self.initial['book']
Or, if you don't want to set the fields, make sure they are optional in your original model:
class Upload(models.Model):
uploader = models.ForeignKey('uploader', on_delete=models.CASCADE, null=True, blank=True)
book = models.ForeignKey('book', on_delete=models.CASCADE, null=True, blank=True)
language = models.ForeignKey('language', on_delete=models.CASCADE, null=True, blank=True)

Django inline forms

I have to models, an Article and a Photo. There is a many2many relation between them. I need to design an Admin form that works better for editing them.
I batch upload photos and the Photo models are created. Then when I write the articles, I want to be able to edit the Photo models as an inline form.
When I set it up as described so far, I get an article edit form, with a selector to click on the name of each photo. I would like to have all unassigned photos shown, with a text box for the cutline on each photo and checkbox. When I put a checkbox and save the article the pictures are associated with that article. I also need to save the updated Photo objects with the text under each photo.
Suggestions for how I should go about this?
Models.py
class Photo(models.Model):
cutline = models.CharField("Cutline", max_length = 1012, null = True)
uploadDate = models.DateField()
imagefile = models.FileField(upload_to=user_directory_path, max_length=250,
class Articles(models.Model):
pictures = models.ManyToManyField(Photo, related_name='article', blank=True)
title = models.CharField("Title", max_length = 255)
story = models.TextField("Article")
Forms.py
from django import forms
from django.forms import BaseInlineFormSet
from django.forms.formsets import BaseFormSet
from django.forms import ModelForm
from django.db.models import Count, Q
from News.models import Articles, Photo
# Create the form class.
class PhotoIForm(ModelForm):
class Meta:
model = Photo
fields = ['cutline', 'imagefile']
class ArticleForm(ModelForm):
class Meta:
model = Articles
fields = ('title', 'story')
class BasePhotoFormSet(BaseInlineFormSet):
def __init__(self, *args, **kwargs):
super(BasePhotoFormSet, self).__init__(*args, **kwargs)
self.queryset = Photo.objects.annotate(articles_count=Count('article')).filter(articles_count=0)
print(a)
Views.py
#login_required
def article_edit(request):
# Create the formset, specifying the form and formset we want to use.
#PhotoFormSet = modelformset_factory(Photo, formset=BasePhotoFormSet, fields = ('cutline', 'imagefile')) #This doesn't work - seems to fail with the m2m fk
PhotoFormSet = modelformset_factory(Photo, fields = ('cutline', 'imagefile')) # close but it doesn't allow me to set up the initial items and limit photos to ones without assigned articles
article = Articles.objects.get(pk = 2718)
if request.method == 'POST':
article_form = ArticleForm(request.POST, article=article)
photo_formset = PhotoFormSet(request.POST)
if article_form.is_valid() and photo_formset.is_valid():
# Save user info
print("Data is valid save"
else:
article_form = ArticleForm(instance=article)
photo_formset = PhotoFormSet()
context = {
'article_form': article_form,
'photo_formset': article_formset,
}
return render(request, 'News/articleInline.html', context)

set slug field form manually in views Django

I'm new in Django and I'm trying to pre fill one of the fields of my form with a slug.
I'm getting the slug from another model. I'm not using ForeignKey because that shows me a list with my objects and I want to save in the form the same slug that I'm using in the url.
Maybe I'm not thinking this right. What should I do?
Thank you!
This are my models:
from django.db import models
class Thing(models.Model):
name = models.CharField(max_length=255,)
rut = models.CharField(max_length=12, blank= True)
cel = models.CharField(max_length=12, blank= True)
slug = models.SlugField(unique=True)
class Control(models.Model):
id_p = models.SlugField()
pa = models.CharField(max_length=3,)
My forms
from django.forms import ModelForm
from collection.models import Thing, Control, Medicamento
class ThingForm(ModelForm):
class Meta:
model = Thing
fields = ('name', 'rut','cel','pet',)
class ControlForm(ModelForm):
class Meta:
model = Control
exclude = ['id_p']
This is what I'm doing in the views
def add_control(request, slug):
thing = Thing.objects.get(slug=slug)
form_class = ControlForm
form_class(initial={'id_p':thing})
if request.method == 'POST':
form = form_class(request.POST)
if form.is_valid():
form.save()
return redirect('thing_detail', slug=thing.slug)
else: form = form_class()
return render(request, 'things/control.html', {
'thing': thing,
'form': form,
})
So, I figure it out!
In views.py, after " if form.is_valid():"
I put this:
prev = form.save(commit=False)
prev.id_p = thing.slug
prev.save()
In that way I put the data in the excluded field before I commit the form.