Widgets in inlineformset_factory - django

Hey i managed to make a inlineformset_factory but my widget in the Parent Model are not working although i have specified them in the ModelForm .
My forms.py :
class PostForm(forms.ModelForm):
post = forms.CharField(widget=CKEditorWidget())
class Meta:
model = Post
fields = ['title', 'author','picture','post','draft','publish']
class PostVocabForm(forms.ModelForm):
class Meta:
model = PostVocab
exclude = ()
PostVocabInlineFormSet = inlineformset_factory(
Post,
PostVocab,
extra=1,
exclude=(),
)
My CKEditorWidget is not working ....
My views.py:
class PostPostVocabCreate(CreateView):
model = Post
form_class = PostForm
# fields = ['title', 'author', 'picture', 'post', 'draft', 'publish']
def get_redirect_url(self, pk):
return reverse_lazy('blog:post_detail',
kwargs={'slug': pk},
)
def get_context_data(self, **kwargs):
data = super(PostPostVocabCreate, self).get_context_data(**kwargs)
if self.request.POST:
data['postvocabs'] = PostVocabInlineFormSet(self.request.POST)
else:
data['postvocabs'] = PostVocabInlineFormSet()
return data
def form_valid(self, form):
context = self.get_context_data()
postvocabs = context['postvocabs']
with transaction.atomic():
self.object = form.save()
if postvocabs.is_valid():
postvocabs.instance = self.object
postvocabs.save()
return super(PostPostVocabCreate, self).form_valid(form)
I guess that my widget from the Parent model (Post) was overwritten while using a inlineformset_factory...

You can set widgets inside of inlineformset_factory.
PostVocabInlineFormSet = inlineformset_factory(
Post,
PostVocab,
extra=1,
exclude=(),
widgets={'post': CKEditorWidget()
)
From Django docs...inlineformset_factory uses modelformset_factory and passes most of its arguments to modelformset_factory. This means you can use the widgets parameter in much the same way as passing it to modelformset_factory.
AuthorFormSet = modelformset_factory(
... Author, fields=('name', 'title'),
... widgets={'name': Textarea(attrs={'cols': 80, 'rows': 20})})`

Related

How to get UpdateView to work with generic FormView

I have a Formview and an UpdateView which are conflicting with each other. They are supposed to work together but not in this way. Whenever I try to use the UpdateView the values get passed through the FormView for saving which causes it to send back form validation errors
e.g. 'A video with this Title already exists'.
Forms.py:
class BaseVideoUploadForm(forms.ModelForm):
"""
Form for uploading videos using related to:
:model: 'videos.Video'.
"""
title = forms.CharField(
widget=forms.TextInput(
attrs={'placeholder': 'Blackjack shuffle procedures'}))
description = forms.CharField(
widget=forms.Textarea(attrs={'rows': 3}),
help_text='A description of the content in this video.')
category = forms.ModelChoiceField(
queryset=Category.objects.all(),
required=True)
receiver = forms.ModelMultipleChoiceField(
queryset=EmployeeType.objects.all(),
required=True,
widget=forms.CheckboxSelectMultiple,
help_text='Employees in these groups will be able to view the video.')
video_file = forms.FileField()
thumbnail = forms.ImageField(required=False)
class Meta:
model = Video
fields = (
'title',
'description',
'receiver',
'category',
'video_file',
'thumbnail',
)
widgets = {
'date_time': forms.HiddenInput(),
'sender': forms.HiddenInput(),
'unviewed': forms.HiddenInput(),
'viewed': forms.HiddenInput(),
}
def __init__(self, *args, **kwargs):
super(BaseVideoUploadForm, self).__init__(*args, **kwargs)
self.fields['receiver'].label = "Viewers"
self.fields['receiver'].queryset = EmployeeType.objects.all()
self.fields['category'].queryset = Category.objects.all()
class VideoUploadForm(BaseVideoUploadForm):
send_notifications = forms.BooleanField(
required=False,
help_text='Send viewers a notification about the new video.')
class Meta(BaseVideoUploadForm.Meta):
fields = BaseVideoUploadForm.Meta.fields + ('send_notifications', )
FormView:
class VideoUploadView(FormView):
form_class = VideoUploadForm
success_url = '/videos'
template_name = 'videos/video_form.html'
def get(self, request, *args, **kwargs):
form = self.form_class()
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST, request.FILES)
form.instance.sender = self.request.user
if form.is_valid():
form.save()
# doing stuff
form.save()
messages.success(self.request, self.success_message)
if request.is_ajax():
return JsonResponse({'success': True, 'url': reverse('videos-home')})
else:
return redirect(self.success_url)
else:
if request.is_ajax():
return JsonResponse({'success': False, 'error': form.errors})
else:
return render(request, self.template_name, {'form': form})
UpdateView:
class VideoUpdateView(UpdateView):
model = Video
form_class = VideoUploadForm
Urls.py:
urlpatterns = [
path('', views.VideoListView.as_view(), name='videos-home'),
path('upload/', views.VideoUploadView.as_view(), name='videos-upload'),
path('<int:pk>', VideoDetailView.as_view(), name='videos-detail'),
path('<int:pk>/delete/', VideoDeleteView.as_view(), name='videos-delete'),
path('<int:pk>/viewed/', views.mark_as_viewed, name='mark-as-viewed'),
path('<int:pk>/update/', VideoUpdateView.as_view(), name='videos-update'),
path('<int:pk>/notify', VideoNotificationView.as_view(), name='videos-notify'),
]
How do I get the UpdateView to not rely on the FormView? Or to work with the FormView? It seems all the UpdateView is doing is pointing to the FormView.

Django Improved UpdateView?

I have this UpDateView class and I need just author of article can edit the blog .I had the solution for the CreateView class(using def Form_valid) but it doesn't work for UpdateView class :::
class ArticleUpdateView(LoginRequiredMixin,UpdateView):
model = models.Article
template_name = 'article_edit.html'
fields = ['title','body']
login_url = 'login'
class ArticleCreateView(LoginRequiredMixin,CreateView):
model = models.Article
template_name = 'article_new.html'
fields = ['title','body',]
login_url='login'
def form_valid(self,form):
form.instance.author = self.request.user
return super().form_valid(form)
You can override the get_object method in your view class:
class ArticleUpdateView(LoginRequiredMixin,UpdateView):
model = models.Article
template_name = 'article_edit.html'
fields = ['title','body']
login_url = 'login'
def get_object(self, *args, **kwargs):
article = super().get_object(*args, **kwargs)
if article.author != self.request.user:
raise PermissionDenied('You should be the author of this article.')
return article

Django - DetailView with FormMixin and initial

I have DetaiView for my post and I want to use a form in this view so I decided to use DetailView with FormMixin. I need to set some initial to this form and I don't know how to do it. Here is my code:
models.py
class Comment(models.Model):
post = models.ForeignKey(Post, related_name="comments", on_delete=models.CASCADE)
name = models.CharField("Nick", max_length=80)
email = models.EmailField()
body = models.TextField("Body")
created = models.DateTimeField("created", auto_now_add=True)
updated = models.DateTimeField("Updated", auto_now=True)
forms.py
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = (
"name",
"email",
"body"
)
views.py
class PostDetailView(FormMixin, DetailView):
model = Post
form_class = CommentForm
template_name = "newspaper/post-detail.html"
def get_success_url(self):
return reverse("post-detail", kwargs={"slug": self.object.slug})
def get_context_data(self, **kwargs):
context = super(PostDetailView, self).get_context_data(**kwargs)
context["form"] = self.get_form()
return context
def post(self, request, *args, **kwargs):
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
return super().form_valid(form)
So I want to post in CommentForm to post of this DetailView. I hope you understand :D.
Thanks in advance for the help!
With FormMixin you can specify form's initial using initial attribute:
class PostDetailView(FormMixin, DetailView):
model = Post
form_class = CommentForm
template_name = "newspaper/post-detail.html"
initial={'name': 'John'}
Or get_initial method:
def get_initial(self):
return {"post": self.get_object() }

How do I prepopulate an inline formset that is purely ImageFields?

I have two models: Profile and CredentialImage.
I have implemented inlineformsets to allow the Profile to upload up to 5 maximum images(CredentialImage).
Currently, the images save to the database but they will not pre-populate when I revisit the update form, thus allowing the user to upload an unlimited amount of photos 5 at a time. Ideally, the same images saved to the database would pre-populate on the form as to limit the Profile to own and edit only 5 photos at a time.
From my understanding, I need to pass in the object to the page to pre-populate information, and I believe I'm doing just that by defining get_object.
Here are the two models:
class Profile(models.Model):
...
def get_absolute_url(self):
return reverse("profile:profile_detail",
kwargs={"username": self.user})
class CredentialImage(models.Model):
profile = models.ForeignKey(Profile, default=None,
related_name='credentialimage')
image = models.ImageField(upload_to=credential_photo_upload_loc)
The modelforms + initialization of the inlineformset_factory:
from django.forms.models import inlineformset_factory
class ProfileUpdateForm(ModelForm):
class Meta:
model = Profile
fields = [
"introduction",
"biography",
]
class CredentialImageForm(ModelForm):
image = ImageField()
class Meta:
model = CredentialImage
fields = ['image', ]
CredentialImageFormSet = inlineformset_factory(Profile,
CredentialImage, fields=('image', ), extra=4, max_num=4)
A class-based UpdateView for updating a Profile:
class ProfileUpdateView(LoginRequiredMixin, UpdateView):
form_class = ProfileUpdateForm
template_name = 'profile/profile_edit.html'
def get_context_data(self, **kwargs):
context = super(ProfileUpdateView, self).get_context_data(**kwargs)
if self.request.POST:
context['credential_image'] = CredentialImageFormSet(self.request.POST, self.request.FILES)
else:
context['credential_image'] = CredentialImageFormSet()
return context
def get_object(self, *args, **kwargs):
user_profile = self.kwargs.get('username')
obj = get_object_or_404(Profile, user__username=user_profile)
return obj
def form_valid(self, form):
data = self.get_context_data()
formset = data['credential_image']
if formset.is_valid():
self.object = form.save()
formset.instance = self.object
formset.save()
return redirect(self.object.get_absolute_url())
instance = form.save(commit=False)
instance.user = self.request.user
return super(ProfileUpdateView, self).form_valid(form)
You have to supply an instance in your get_context_data method.
def get_context_data(self, **kwargs):
context = super(ProfileUpdateView, self).get_context_data(**kwargs)
if self.request.POST:
context['credential_image'] = CredentialImageFormSet(self.request.POST, self.request.FILES, instance=self.object)
else:
context['credential_image'] = CredentialImageFormSet(instance=self.object)
return context

Why aren't the images of an inlineformset_factory being saved?

I have two models: Profile and CredentialImage.
I am trying to allow each Profile to upload, optionally, up to 5 maximum images(CredentialImage).
I've decided to use an inlineformset_factory for the images because on the UpdateView users will be given the option of updating their general Profile information as well as their 5 select images.
The code goes without error, but the images do not save to the database.
Here are the two models:
class Profile(models.Model):
...
def get_absolute_url(self):
return reverse("profile:profile_detail",
kwargs={"username": self.user})
class CredentialImage(models.Model):
profile = models.ForeignKey(Profile, default=None)
image = models.ImageField(upload_to=credential_photo_upload_loc)
The modelforms + initialization of the inlineformset_factory:
from django.forms.models import inlineformset_factory
class ProfileUpdateForm(ModelForm):
class Meta:
model = Profile
fields = [
"introduction",
"biography",
]
class CredentialImageForm(ModelForm):
image = ImageField()
class Meta:
model = CredentialImage
fields = ['image', ]
CredentialImageFormSet = inlineformset_factory(Profile,
CredentialImage, fields=('image', ), extra=4)
A class-based UpdateView for updating a Profile:
class ProfileUpdateView(LoginRequiredMixin, UpdateView):
form_class = ProfileUpdateForm
template_name = 'profile/profile_edit.html'
def get_context_data(self, **kwargs):
context = super(ProfileUpdateView, self).get_context_data(**kwargs)
if self.request.POST:
context['credential_image'] = CredentialImageFormSet(self.request.POST)
else:
context['credential_image'] = CredentialImageFormSet()
return context
def get_object(self, *args, **kwargs):
user_profile = self.kwargs.get('username')
obj = get_object_or_404(Profile, user__username=user_profile)
return obj
def form_valid(self, form):
data = self.get_context_data()
formset = data['credential_image']
if formset.is_valid():
self.object = form.save()
formset.instance = self.object
formset.save()
return redirect(self.object.get_absolute_url())
instance = form.save(commit=False)
instance.user = self.request.user
return super(ProfileUpdateView, self).form_valid(form)
I'm especially wary of the get_context_data and form_valid.
Is it correct to try and instantiate the formset using get_context_data and to save both within form_valid?
You need to pass request.FILES to the formset as well as request.POST when you are uploading files:
context['credential_image'] = CredentialImageFormSet(self.request.POST, self.request.FILES)
The get_context_data method is meant for getting the context for the data. You shouldn't be instantiating formsets there. You could have a look at the UpdateWithInlinesView from django-extra-views.