Django inline forms - django

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)

Related

Django - how to make tags on a per user basis

on a Django project that uses django-taggit (https://pypi.org/project/django-taggit/)
I would like to make tags on a per user basis, this way each user can define its own set of tags.
I'm settings up the following model:
# models.py
from django.db import models
from taggit.models import Tag
from django.contrib.auth.models import User
# Create your models here.
class MyTag(Tag):
""" You must make taggit.models.Tag an abstract model"""
user = models.ForeignKey(User, related_name="to_tags", on_delete=models.SET_NULL)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
def save(self, args, kwargs):
user = kwargs.pop('user')
self.user = user
super(MyTag, self).save(*args, **kwargs)
then to manage I'd use a normal form ( in this case the tags are for a Photo model)
# forms.py
class PhotoForm(forms.ModelForm):
class Meta:
model = Photo
fields = ('name', 'artistic_name', 'description', 'tags', 'is_top', 'note')
widgets = {
'description': forms.Textarea(attrs={'rows': 2}),
'note': forms.Textarea(attrs={'rows': 2})
}
now the Question...how to I save the user in the MyTag model?
I have to pass it to the form instance in the view doing something like:
def photo_detail(request, photo_id):
...
form = PhotoForm(request.POST or None, user=request.user)
...
if request.method == 'POST':
if form.is_valid():
form.save()
...
first question...should I pass the user when I make the form instance, or when I call the save method...?
Then I would have to intercept the Tag.save()...but here I'm lost.
Any help is appreciated.
Thank you very much!
Carlo

Combine DetailView and UpdateView?

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...

Django forms dynamic getting author as a logged in user in model forms

I'm trying to make some forms that will allow users to add some objects, delete them or edit but I've stucked with thing like author of model. Let's say we got model Shot which got field
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
Because I've created custom user model to expand user by some fields that I want, and then we creating modelForm, creating views etc. and finally got form. When we will try to submit this form, it won't add this object submited in form to db because form has no filled field author author which means this field == Null and that's why it won't add this to db. So my question is how to get it dynamic, for example when user with nick "thebestuser" will try to add this modelForm it will work and mark author as "thebestuser"? Ofc I could add to form field author, but it's the worst way in my opinion and every user would be allowed then to add object for example as a another user, let's say user with nick "anothernick" could add form as a user with "thebestuser" which is In my opinion not acceptable.
models.py
from django.db import models
from django.contrib.auth.models import User
from streamers.models import Streamer
from django.conf import settings
from django.utils import timezone
class Shot(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(max_length=70)
url = models.CharField(max_length=100)
streamer = models.ForeignKey(Streamer, on_delete=models.CASCADE)
published_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
forms.py
from django import forms
from .models import Shot
class AddShot(forms.ModelForm):
class Meta:
model = Shot
fields = [
'title',
'url',
'streamer',
]
views.py
#login_required
def add_shot(request):
form = AddShot(request.POST or None)
if form.is_valid():
form.save()
instance = form.save(commit=False)
instance.published_date = request.published_date
instance.author = request.user
instance.save()
context = {
'form': form
}
return render(request, 'shots/add_shot.html', context)
You'll need to do it in your view. When you save your form pass commit=False to your save method, add your user, then save the returned instance.
def my_view(request):
form = AddShot(request.POST)
instance = form.save(commit=False)
instance.author = request.user
instance.save()
Documented here: https://docs.djangoproject.com/en/2.1/topics/forms/modelforms/#the-save-method

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.

How can I upload multiple files to a model field?

I want to upload multiple files through a ModelForm,with all files to be assigned to a file field of the Model.I have gone through the docs and I saw an example on it and I ve implemented it here but I can only get my form to pick multiple files but only one get saved and assigned to filesfield.Below are my codes
models.py
class Feed(models.Model):
user=models.ForeignKey(User,on_delete=models.CASCADE,related_name='feeds')
text=models.TextField(blank=False,max_length=500)
files = models.FileField(upload_to="files/%Y/%m/%d")
forms.py
class FeedForm(ModelForm):
class Meta:
model=Feed
fields=('text','auth','files')
widgets={"files":forms.FileInput(attrs={'id':'files','required':True,'multiple':True})}
and views.py
def post_feed(request):
form_class = FeedForm
if request.method == 'POST':
form = form_class(request.POST,request.FILES)
if form.is_valid():
feed = form.save(commit=False)
feed.user = User.objects.get(pk=1)
feed.pub_date=timezone.now()
#instance = Feed(files=request.FILES['files'])
# feed.files=request.FILES['files']
feed.save()
return redirect('home')
else:
form = form_class()
return render(request, 'post_feed.html', {'form': form,})
from django.views.generic.edit import FormView
from .forms import FeedForm
class FileFieldView(FormView):
form_class=FeedForm
template_name='post_feed.html'
'''success_url=??? #I dont know what to write here.I thought of putting this
render(request, 'post_feed.html', {'form': form,}) because I just want
to reload the page but it gave an error,so I removed it entirely.'''
def post_feed(self,request,*args,**kwargs):
form_class=self.get_form_class()
form=self.get_form(form_class)
filez=request.FILES.getlist('files')
if form.is_valid():
for f in filez:
f.save()
return self.form_valid(form)
else:
return self.form_invalid(form)
Kindly help me out,Thanks in advance.
You have to create a separate model for the files and connect them with a foreign key:
class Feed(models.Model):
user=models.ForeignKey(User, on_delete=models.CASCADE, related_name='feeds')
text=models.TextField(blank=False, max_length=500)
class FeedFile(models.Model):
file = models.FileField(upload_to="files/%Y/%m/%d")
feed = models.ForeignKey(Feed, on_delete=models.CASCADE, related_name='files')
I hope this helps.
Phew, it took me a whole day to figure out this. My goal was to assign multiple files to one instance of a class, like a Blog instance can have multiple Images. First things first, you cannot do this with one models.FileField inside a model (for example inside Blog class), because this field was not designed to save multiple files. So the solution is to create separate model for the files and connect them with One-to-Many Relationship (Foreign Key) as it was answered by #Carlos Mermingas.
Enough words, here is the code for the above situation:
# models.py
class Feed(models.Model):
user=models.ForeignKey(User, on_delete=models.CASCADE)
text=models.TextField(blank=False, max_length=500)
class FeedFile(models.Model):
file = models.FileField(upload_to="files/%Y/%m/%d")
feed = models.ForeignKey(Feed, on_delete=models.CASCADE)
# forms.py
...
from django.forms import ClearableFileInput
...
class FeedModelForm(forms.ModelForm):
class Meta:
model = Feed
fields = ['text']
class FileModelForm(forms.ModelForm):
class Meta:
model = FeedFile
fields = ['file']
widgets = {
'file': ClearableFileInput(attrs={'multiple': True}),
}
# widget is important to upload multiple files
# views.py
from .models import FeedFile
...
def create_to_feed(request):
user = request.user
if request.method == 'POST':
form = FeedModelForm(request.POST)
file_form = FileModelForm(request.POST, request.FILES)
files = request.FILES.getlist('file') #field name in model
if form.is_valid() and file_form.is_valid():
feed_instance = form.save(commit=False)
feed_instance.user = user
feed_instance.save()
for f in files:
file_instance = FeedFile(file=f, feed=feed_instance)
file_instance.save()
else:
form = FeedModelForm()
file_form = FileModelForm()
# the rest is the basic code: template_name, context, render etc.
# in your template.html <form> tag must include enctype="multipart/form-data"
Bonus: if you want to see uploaded files in admin panel, you can use InlineModelAdmin objects. Here is the code:
# admin.py of your app
from django.contrib import admin
from .models import Feed, FeedFile
class FeedFileInline(admin.TabularInline):
model = FeedFile
class FeedAdmin(admin.ModelAdmin):
inlines = [
FeedFileInline,
]
admin.site.register(Feed, FeedAdmin)
For the more details on file upload, Model Forms, how to include widget in Model Form
Would suggest using an M2M field from Feed model to FeedFile model.
Makes it all the more easier while querying for files of a particular Feed object, which i feel is also the most common usecase for Feed objects
class Feed(models.Model):
user=models.ForeignKey(User, on_delete=models.CASCADE, related_name='feeds')
text=models.TextField(blank=False, max_length=500)
files=models.ManyToManyField(FeedFile)
class FeedFile(models.Model):
file = models.FileField(upload_to="files/%Y/%m/%d")