views.py
class PostCreatView(LoginRequiredMixin, CreateView):
model = Posts
fields = ['image', 'caption']
template_name = 'home/creat-post.html'
success_url = '/home/'
def form_valid(self,form):
form.instance.user = self.request.user
return super().form_valid(form)
models.py
class Posts(models.Model):
caption = models.CharField(max_length=2200)
date_posted = models.DateTimeField(default=timezone.now())
image = models.ImageField( upload_to='PostsImages')
user = ForeignKey(User, on_delete=models.CASCADE ,related_name='UserPosts')
def __str__(self):
return f"Post {self.id} ({self.user.username})'s"
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
img = Image.open(self.image.path)
#img = make_square(img, 1080, 1080)
img.save(self.image.path)
class Meta:
ordering = ['-date_posted']
creat-post.html
{% extends "home/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div>
<form method="POST">
<fieldset>
<legend >Creat Post</legend>
{% csrf_token %}
{{ form|crispy }}
</fieldset>
<button class="btn btn-primary" type="submit">Post</button>
</form>
</div>
{% endblock %}
So I created a creatview to add create new post. However when I go to the link and add photo then hit submit, it gives me as I haven't uploaded an image.
However, I am able to add images to a new Post from the admin normally.
Try to add enctype in your template:
<form method="post" enctype="multipart/form-data">
Related
I am attempting to add a form for comments to a DetailView. The DetailView displays notes for specific projects. So the comments have a foreign key that is the specific note and the note has a foreign key for the specific project.
I am attempting to use FormMixin with DetailView. So far I have not bee successful. Currently I can get the form to display but it does not save and in the terminal I see the following error Method Not Allowed (POST): /projects/project/1/note/1/
I can get these to work separately but not with the form in the DetailView.
Here are my models:
class ProjectNotes(models.Model):
title = models.CharField(max_length=200)
body = tinymce_models.HTMLField()
date = models.DateField(auto_now_add=True)
project = models.ForeignKey(Project, default=0, blank=True, on_delete=models.CASCADE, related_name='notes')
def __str__(self):
return self.title
class ProjectNoteComments(models.Model):
body = tinymce_models.HTMLField()
date = models.DateField(auto_now_add=True)
projectnote = models.ForeignKey(ProjectNotes, default=0, blank=True, on_delete=models.CASCADE, related_name='comments')
The View:
class ProjectNotesDetailView(DetailView, FormMixin):
model = ProjectNotes
id = ProjectNotes.objects.only('id')
template_name = 'company_accounts/project_note_detail.html'
comments = ProjectNotes.comments
form_class = NoteCommentForm
def form_valid(self, form):
projectnote = get_object_or_404(ProjectNotes, id=self.kwargs.get('pk'))
comment = form.save(commit=False)
comment.projectnote = projectnote
comment.save()
return super().form_valid(form)
def get_success_url(self):
return reverse('project_detail', args=[self.kwargs.get('pk')])
The form:
class NoteCommentForm(forms.ModelForm):
class Meta:
model = ProjectNoteComments
fields =['body',]
widgets = {
'body': forms.TextInput(attrs={'class': 'form-control'})
}
The template:
% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="section-container container">
<div class="project-entry">
<h2>{{ projectnotes.title }}</h2>
<p>{{ projectnotes.body | safe }}</p>
</div>
<div><b>Comments on {{projectnotes.title}}</b></div>
{% if projectnotes.comments.all %}
{% for comment in projectnotes.comments.all %}
<div class="notecomments" style="padding: 10px;">
{{ comment.body | safe }}
</div>
{% endfor %}
{% else %}
<p>No comments have been have been added yet.</p>
{% endif %}
<h2>add note</h2>
<h1>Add Comment</h1>
<form action="" method="post">
{% csrf_token %}
{{ form.media }}
{{ form|crispy }}
<input type="submit" value="save">
</form>
{% endblock content %}
Try to change the order between DetailView and FormMixin in ProjectNotesDetailView then implement the post method (enabled by the `FormMixin):
class ProjectNotesDetailView(FormMixin, DetailView):
model = ProjectNotes
id = ProjectNotes.objects.only('id')
template_name = 'company_accounts/project_note_detail.html'
comments = ProjectNotes.comments
form_class = NoteCommentForm
def form_valid(self, form):
projectnote = get_object_or_404(ProjectNotes, id=self.kwargs.get('pk'))
comment = form.save(commit=False)
comment.projectnote = projectnote
comment.save()
return super().form_valid(form)
def get_success_url(self):
return reverse('project_detail', args=[self.kwargs.get('pk')])
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return HttpResponseForbidden()
self.object = self.get_object()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
Check how to use formmixin with detailview (documentation).
Hi i'm trying to add some funcionality to my blog post app, i'd like to paste image into post content so i figured is this waw that ive created new model just for images and set it as OneToOne with my Post model
i wonder if ther is any way to set this image to content field in the post model
models.py
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
class PostImage(models.Model):
post = models.OneToOneField(Post, on_delete=models.CASCADE)
image = models.ImageField(default=None, upload_to='post_pics', blank=True)
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
img = Image.open(self.image.path)
if img.height > 500 or img.width > 500:
output_size = (500, 500)
img.thumbnail(output_size)
img.save(self.image.path)
vievs.py
def home(request):
context = {
'posts': Post.objects.all(),
'user_posts': "active",
}
return render(request, 'blog/home.html', context)
class PostListView(ListView):
model = Post
template_name = 'blog/home.html'
context_object_name = 'posts'
ordering = ['-date_posted']
paginate_by = 5
post_template.html
{% extends "blog/base.html" %}
{% load static %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Blog Post</legend>
{{form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Post</button>
</div>
</form>
</div>
{% endblock content %}
settings.py
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
Well i've found another solution i've used CKEditor and now everything works fine
pip install pillow
& in static/post_pics (see the img.jpg ...png ) it's save ?
Models.py
class Post(models.Model):
title = models.CharField(max_length=100)
conent = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to="post_pics")
def __str__(self):
return self.title
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
img = Image.open(self.image.path)
if img.height > 300 or img.width > 300:
output_size = (300, 300)
img.thumbnail(output_size)
img.save(self.image.path)
class Tag(models.Model):
name = models.CharField(max_length=30)
members = models.ManyToManyField(Post, through='PostTag')
def __str__(self):
return self.name
class PostTag(models.Model):
article = models.ForeignKey(Post, on_delete=models.CASCADE)
tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
Forms.py
class PostUpdateForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'conent', 'image']
class TagUpdateForm(forms.ModelForm):
class Meta:
model = PostTag
fields = ['tag']
TagUpdateFormSet = modelformset_factory(PostTag, form=TagUpdateForm)
Views.py
#login_required
def updateView(request, pk):
template_name = 'project/post_update.html'
heading_message = 'Tags'
if request.method == 'GET':
form1 = PostUpdateForm(instance=Post.objects.filter(id=pk).first())
form2 = TagUpdateFormSet(queryset=PostTag.objects.filter(article=Post.objects.filter(id=pk).first()))
elif request.method == 'POST':
form1 = PostUpdateForm(request.POST, request.FILES, instance=Post.objects.filter(id=pk).first())
form2 = TagUpdateFormSet(request.POST, queryset=PostTag.objects.filter(article=Post.objects.filter(id=pk).first()))
if form1.is_valid():
invalid = True
for formz in form2:
if formz.is_valid() == False:
invalid = False
break
if invalid:
postform = form1.save(commit=False)
postform.author = request.user
postform.save()
for forms in form2:
data = forms.cleaned_data.get('tag')
if data:
returnedTag, created1 = Tag.objects.get_or_create(name = data)
link, created2 = PostTag.objects.get_or_create(article = postform, tag = returnedTag)
return HttpResponseRedirect('/forum/post/%s/' %(postform.id))
return render(request, 'project/post_update.html', {'form': form1, 'form2': form2})
template
{% extends "template/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
{% load static %}
<div class="container form-section s-yellow section">
<form method="POST" action="" enctype='multipart/form-data'>
{% csrf_token %}
<fieldset class="form-container">
<legend class="form-top"></legend>
<div class="form1">
{{ form|crispy }}
</div>
<div class="form2 form-group">
{{ form2.management_form }}
{% for formz in form2 %}
<div>
<div class="form-row">
<div class="input-group">
{{formz }}
<div class="">
<button type="button" class="add-form-row">+</button>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Post</button>
</div>
</fieldset>
</form>
</div>
{% endblock content %}
I want to let the user be able to edit all tags in a post, however when i display the tags they show up as choice field that lets the user choose from already existing tags and they can't type a new one.
Is there a way to change the choice field to an input field where the original value is the tag originaly entered.
I tried edditing the 'tag' field to a charfield above the meta tag in the form, but then i was getting an input field with the choice field value (eg 4, 6, 10,) instead of the actual text.
I know the saving part doesnt work, but i need to figure out a way to get the fields dispalyed properly
Btw are there any tutorials for a database structure like this? It's hard to find examples where one table links to 2 others.
I am following buckys django tutorial, he explained how to add albums but not song, i want to try adding songs to specific album but it is not working.
I have added a SongCreate view to my views.py, i also created a song_form.html template for inputting song details.I have also tried the form_valid method, but it doesnt just work.
My views.py
class AlbumCreate(CreateView):
model=Albums
fields=['alb_title','alb_genre','alb_logo','alb_artist']
class SongCreate(CreateView):
model = Song
fields = ['song_title', 'song_format']
def form_valid(self, form):
album = get_object_or_404(Albums, pk=self.kwargs['pk'])
form.instance.album = album
return super(SongCreate, self).form_valid(form)
#My urls.py
app_name ='music'
urlpatterns = [
path('',views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
path('album/add/', views.AlbumCreate.as_view(), name='album-add'),
path('album/<int:pk>/songadd/', views.SongCreate.as_view(),
name='song-add'),
path('album/<int:pk>/', views.AlbumUpdate.as_view(), name='album-
update'),
path('album/<int:pk>/delete/', views.AlbumDelete.as_view(),
name='album-delete'),
]
#My song_form.html template
{% extends 'music/base.html' %}
{% block title %}Add New Song{% endblock %}
{% block body %}
<div class="container-fluid">
<h4>Add the details of the Song in the given fields.</h4>
<div class="row">
<div class="col-sm-12 col-md-7">
<div class="panel panel-default">
<div class="panel-body">
<form class='form-horizontal' action=""
method="post"
enctype="multipart/form-data">
{% csrf_token %}
{% include 'music/form-template.html' %}
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-
success">Submit</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
#Models.py
class Albums(models.Model):
alb_title = models.CharField(max_length = 250)
alb_genre = models.CharField(max_length = 250)
alb_logo = models.FileField()
alb_artist = models.CharField(max_length = 250)
def get_absolute_url(self):
return reverse("music:detail", kwargs={"pk": self.pk})
def __str__(self):
return self.alb_title + '-' + self.alb_artist
class Song(models.Model):
song = models.ForeignKey(Albums, on_delete = models.CASCADE)
song_title = models.CharField(max_length = 250)
song_format = models.CharField(max_length = 10)
is_favourite = models.BooleanField(default=False)
def __str__(self):
return self.song_title
def get_absolute_url(self):
return reverse('music:detail', kwargs={'pk': self.song.pk})
When add song is clicked it directs me to a form to fill song detail, but when i tried it i got:
IntegrityError at /music/album/song/add/
NOT NULL constraint failed: music_song.song_id
you must :
def create_song(request):
if request.method == "POST":
form.save()
I give you the way I use form_valid()
def form_valid(self, form):
album = get_object_or_404(Albums, pk=self.kwargs['pk'])
form = form.save(commit=False)
form.album = album
form.save()
return super().form_valid(form)
If it doesn't work, it means you have changes not reflected in your database. Run ./manage.py makemigrations and ./manage.py migrate. It might be necessary, if possible, to drop and recreate the whole db.
I am using Django CreateView on model object. The form gets rendered but on submitting Post button, nothing happens. In console I am receiving code 200 (Success) but object is not created. Also, I am using same HTML template and same code for Update View and it is working perfectly. Please help.
class EventCreateView(LoginRequiredMixin, CreateView):
model = Event
fields = ['name', 'event_attendees']
def form_valid(self, form):
form.instance.creator = self.request.user
return super().form_valid(form)
Model
name = models.CharField(max_length=100)
date = models.DateTimeField(default=timezone.now)
location = models.CharField(max_length=16, choices=EVENT_VENUES, default='sec-1, noida')
event_attendees = models.FileField(upload_to='documents/', default='')
creator = models.ForeignKey(User, on_delete=models.CASCADE)
form_rollout_time = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('event-detail', kwargs={'pk': self.pk})
Html template
{% extends "events/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">New Event</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Post</button>
</div>
</form>
</div>
{% endblock content %}
Urls
urlpatterns = [
path('', EventListView.as_view(), name='event-home'),
path('user/<str:username>', UserEventListView.as_view(), name='user-events'),
path('event/<int:pk>/', EventDetailView.as_view(), name='event-detail'),
path('event/new/', EventCreateView.as_view(), name='event-create'),
path('event/<int:pk>/update/', EventUpdateView.as_view(), name='event-update'),
path('event/<int:pk>/delete/', EventDeleteView.as_view(), name='event-delete')
]
update code like this
def form_valid(self, form):
obj = form.save(commit=False)
obj.creator = self.request.user
obj.save()
try this
Solved the issue by overriding method form_invalid(). Apparently, the issue was in the implementation with FileField but was suppressed with the default implementation of form_invalid. On overriding the method, the actual issue arose.