Django auto-generated unique field failing validation on form edit - python

I am attempting to reuse my create form (EntryForm) for editing a model in Django. My Entry model has a unique slug that is generated on save. This works fine when creating an Entry, but shows the following error when I attempt to edit it:
Entry with this Slug already exists.
I saw several similar questions, but most were failing to set instance= when instantiating the form. I'm pretty sure I'm doing that part correctly.
I've removed other model fields from the code below for clarity.
Here is my model:
class Entry(models.Model):
title = models.CharField(max_length=128, blank=True)
slug = models.SlugField(unique=True, blank=True)
def save(self, *args, **kwargs):
if not self.title:
self.title = self.date.strftime('%B %-d, %Y')
self.slug = slugify(self.title)
super(Entry, self).save(*args, **kwargs)
My view:
def edit_entry(request, entry_slug):
entry = get_object_or_404(Entry, slug=entry_slug)
form = EntryForm(instance=entry, label_suffix='')
if request.method == 'POST':
form = EntryForm(request.POST, instance=entry, label_suffix='')
if form.is_valid():
form.save(commit=True)
return index(request)
else:
print(form.errors)
return render(request, 'journal/entry/form.html', {'form': form})
My form:
class EntryForm(forms.ModelForm):
title = forms.CharField(required=False, max_length=128, label="Title (defaults to date)")
slug = forms.CharField(widget=forms.HiddenInput(), required=False)
class Meta:
model = Entry
exclude = ()
Any ideas?

I did finally figure this out.
The issue was stemming from the fact that I was reusing my create template for the edit form, but forgot to set the form action dynamically depending on which action I desired. So, my 'edit' form was rendering correctly, but actually submitting via the 'create' action.
Thanks to those who commented and to what ultimately led me to debugging the problem, this handy code snippet equivalent to Ruby's binding.pry:
import code; code.interact(local=dict(globals(), **locals()))
I did also take #xyres's advice and remove the slug from my form, as it was unnecessary.
New form:
class EntryForm(forms.ModelForm):
title = forms.CharField(required=False, max_length=128, label="Title (defaults to date)")
class Meta:
model = Entry
exclude = ['slug']
New final line of edit_entry():
return render(request, 'journal/entry/form.html', {'form': form, 'entry': entry})
Form action:
{% if entry %}
<form id="entry_form" method="post" action="/journal/entry/{{ entry.slug }}/edit">
{% else %}
<form id="entry_form" method="post" action="/journal/new_entry/">
{% endif %}

Related

Django - how add User specific Items?

Good day Stackoverflow,
a user should be able to add multiple titles instead of always overwriting the one added title.
\\ views.py
def edit_profile(request):
try:
profile = request.user.userprofile
except UserProfile.DoesNotExist:
profile = UserProfile(user=request.user)
if request.method == 'POST':
form = UserProfileForm(request.POST, instance=profile)
if form.is_valid():
form.save()
return redirect('/test')
else:
form = UserProfileForm(instance=profile)
return render(request, 'forms.html', {'form': form, 'profile': profile})
\\models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
title = models.CharField(max_length=1024)
def __str__(self):
return str(self.title)
\\forms.py
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('title',)
Then the user has a form on the website where he can add the specific title.
Until now, however, every time the user fills out the title form, the value in the database is overwritten.
As it should be:
When a new title is added in the form, it should simply be added to it.
At the end I should have the possibility, with a Foor loop in the HTML template, to display all the added titles of the respective user.
Do you know how to do this?
If you are using a relational database, this functionality isn't really supported for a single field. Though, if you really wanted to, you could use a JSON field to make this work.
However, it is probably a better idea to use a separate table for titles.
To do this, you need to create a new Title object like:
class Title(models.Model):
Then, create a many-to-one relationship using ForeignKey:
class Title(models.Model):
text = models.CharField(max_length=1024)
user_profile = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
The on_delete method is required. This particular one will delete all Titles associated with a UserProfile if a UserProfile is deleted.
Now, if you want to associate a title object with a UserProfile, you would do it like this:
profile = UserProfile(user=request.user)
title = Title.objects.create(text='My Very First Title', user_profile=profile)
For more info on many-to-one relationships in Django: https://docs.djangoproject.com/en/4.0/topics/db/examples/many_to_one/
You can create new model and assign new with the ForeignKey field.
models.py:
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
def __str__(self):
return ', '.join([title for title in self.titles.all()])
class UserTitle(models.Model):
title = models.CharField(max_length=1024)
userprofile = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name='titles')
def __str__(self):
return self.title
views.py:
def edit_profile(request):
...
if request.method == 'POST':
...
if form.is_valid():
form.instance.userprofile = request.user.userprofile
form.save()
return redirect('/test')
...
admin.py:
from django.contrib import admin
from your_app.models import UserProfile
class UserProfileAdmin(admin.ModelAdmin):
list_display = ['id', 'user', '__str__']
admin.site.register(UserProfile, UserProfileAdmin)
settings.py:
INSTALLED_APPS = [
...
'your_app',
...
In template, to make for loop just use:
{% for title in user.userprofile.titles.all %}
{{ title }}
{% endfor %}
or if you need only User titles in single string:
{{ user.userprofile }}

Django not saving form data

I've made a simple test django project that's a simple list of posts. And I'm trying to add functionality to update a post. Everything seems to be working, except the edits aren't saved to the database.
I've checked the cleaned data to see if the updated data is coming through, and it is, but the save() function doesn't seem to actually do anything.
models.py
class Block(models.Model):
title = models.CharField(max_length=255)
slug = models.SlugField(unique=True, max_length=140, null=True, blank=True)
content = models.TextField()
def save(self, *args, **kwargs):
if self.slug is None:
self.slug = get_unique_slug(self, 'title', 'slug')
super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse("bulkapp:one_view", kwargs={"slug_id": self.slug})
def __str__(self):
return self.title
views.py
def edit_block_view(request, slug_id):
single_block_query = get_object_or_404(Block, slug=slug_id)
update_form_query = BlockForm(request.POST or None, instance=single_block_query)
if update_form_query.is_valid():
update_form_query.save()
return redirect('bulkapp:one_view', slug_id=slug_id)
return render(request, 'bulkapp/update.html', {'update_form': update_form_query})
<form class="form-container" method="POST">
{% csrf_token %}
{{update_form.as_p}}
<input type="submit" value="Edit">
</form>
Edit:
forms.py
class BlockForm(forms.ModelForm):
class Meta:
model = Block
fields = [
"title",
"slug",
"content",
]
The redirect fires as expected, but no changes are saved, and no error messages are written to the console. Any help would be much appreciated.
The problem was the indentation of the super().save(*args, **kwargs) in my save() function which didn't execute if it the slug was not null.

Django: form is not displaying

After the user registers, I am using signals to create a user profile, so they can fill in additional details (like country or website) that the standard registration form can't offer. The problem is the form is not displaying the contents. However I can fill in the same form on admin site easily, but I want to allow users to do it by themselves.
On Chrome I tried to inspect the problem, and where there is a form it says input='hidden'. What might be the problem?
Thank you in advance.
my views:
def edit_profile(request):
if request.method == 'POST':
edit_form = EditProfileForm(request.POST,
instance=request.user.profile)
if edit_form.is_valid():
edit_form.save()
messages.success(request, 'Your account has been updated!')
return redirect(profile)
else:
edit_form = EditProfileForm(instance=request.user.profile)
context = {'edit_form': edit_form}
return render(request, 'profile.html', context)
my profile.html:
<form method='POST'>
{% csrf_token %}
{{ edit_form }}
<button type="submit">Update</button>
</form>
models:
class Profile(models.Model):
user = models.OneToOneField(User)
description = models.CharField(max_length=100, default='')
country = models.CharField(max_length=100, default='')
website = models.URLField(default='')
image = models.ImageField(default='images/profile.jpg',
upload_to='images')
def __str__(self):
return self.user.username
def create_profile(sender, **kwargs):
if kwargs['created']:
profile = Profile.objects.create(user=kwargs['instance'])
post_save.connect(create_profile, sender=User)
forms.py:
class EditProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('status', 'website', 'description', 'country', 'user', )
Do you get an indentation error when you run your code?
If not can you please update your question with the correct indentation so that the SO people can help you troubleshoot.
Next up, do you have any group permissions in place for standard users vs non-standard user(admin)? I would check to see if they have the capability to modify their User objects freely.
Errors for the form are usually displayed by <ul class="errorlist"> FormsAPI, I would check for those. If none, then I would double check that the form being returned isn't actually blank by debugging it and looking at the __dict__ definition of that object. I can't give you any more suggestions until the indentation part is fixed so I can rule out any logic errors.
Found the problem: edit_form = EditProfileForm(instance=request.user.profile)
What you want to do is query the Profile object that belongs to the user, bold then pass it in as an instance to your EditProfileForm.
class EditProfileForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('status', 'website', 'description', 'country', 'user', )
Your form's Meta is the Profile object not the User object.
Hope that helps, good luck!

Image is not uploading via form

I am making django project and have one problem with uploading images in forms.
So, I tried to add one object with image, in admin this is working, but in form on site - not.
My views:
def newbook(request, user_id):
form = BookAdd(request.POST or None)
if form.is_valid():
book = form.save(commit=False)
book.author = get_object_or_404(User, pk=user_id)
book.save()
return redirect('../%s' % book.id)
return render(request, 'userbook/newbook.html', {'form': form})
My model:
class Book(models.Model):
"""Book is a compilation of sections with subjects."""
author = models.ForeignKey(AUTH_USER_MODEL)
name = models.CharField(max_length=100)
description = models.CharField(blank=True, max_length=256)
cover = models.ImageField(upload_to='img/bookcovers')
def __str__(self):
return self.name
My form:
class BookAdd(ModelForm):
class Meta:
model = Book
fields = ('name', 'description', 'cover')
When I add new book, I get an error "the field is required", maybe for field of cover, but image added. This work honestly on local server, but don't work on pythonanywhere.com
You have to change code
form = BookAdd(request.POST or None)
to
form = BookAdd(request.POST,request.FILES)
and your form should have enctype="multipart/form-data"
<form action="." method="post" enctype="multipart/form-data">

save method in a view

I have a very simple model:
class Artist(models.Model):
name = models.CharField(max_length=64, unique=False)
band = models.CharField(max_length=64, unique=False)
instrument = models.CharField(max_length=64, unique=False)
def __unicode__ (self):
return self.name
that I'm using as a model form:
from django.forms import ModelForm
from artistmod.artistcat.models import *
class ArtistForm(ModelForm):
class Meta:
model = Artist
but I can't seem to construct a view that will save the form data to the database. Currently I'm using:
def create_page(request):
if request.method == 'POST':
form = ArtistForm(request.POST)
if form.is_valid():
form.save()
return render_to_response('display.html')
else:
form = ArtistForm()
return render_to_response('create.html', {
'form': form,
})
can anyone help the newbie?
Apparently the problem resided in my template. I was using
<form action="display/" method="POST">
as opposed to
<form action="." method="POST">
also changed my HttpRequest object from render_to_response to HttpResponseRedirect
true newbie errors but at least it works now

Categories