Django SlugField not unique - django

I'm stuck from a couple hours and I can't solve this problem.
The following code works well, but if I write a "Title" that already exist I get:
UNIQUE constraint failed: appname_shopaccount.url_shop
Model
class ShopAccount(models.Model):
user = models.ForeignKey(User)
title = models.CharField(max_length=150)
url_shop = models.SlugField(max_length=200, unique=True)
company_name = models.CharField(max_length=200)
def save(self, *args, **kwargs):
self.url_shop = slugify(self.title)
super(ShopAccount, self).save(*args, **kwargs)
def __str__(self):
return self.title
Forms
class SignUpShopForm(ModelForm):
class Meta:
model = ShopAccount
fields=['title', 'company_name']
exclude= ('user',)
error_messages = {
'title': {
'required': "Enter a Shop name",
},
'company_name': {
'required': "Enter a Company name",
}
}
View
def signup_shop(request):
if request.POST:
form = SignUpShopForm(request.POST)
if form.is_valid():
account = form.save(commit=False)
account.user = request.user
account.save()
return HttpResponseRedirect('/account/updated/')
else:
form = SignUpShopForm(data=request.POST)
return render_to_response('register_shop.html', { 'form':form }, context_instance=RequestContext(request))
else:
return render_to_response('register_shop.html', context_instance=RequestContext(request))
How can I solve this problem?

Remove unique=True from the url_shop field in your model and update your database with python manage.py makemigrations and python manage.py migrate.
Be aware though that a slug field that can have non-unique values might have consequences for your app depending on how you are using it (two objects having the same url for example)

You can use unique_slugify to force a unique slug- https://djangosnippets.org/snippets/690/
import unique_slugify
class ShopAccount(models.Model):
...
def save(self, *args, **kwargs):
self.url_shop = unique_slugify(self, self.title, slug_field_name='url_shop')
super(ShopAccount, self).save(*args, **kwargs)
If the slug being created already exists, unique_slugify will append a '-1' (and on upwards) to the slug until a unique slug is found. Keep unique=True in your url_shop model kwargs :]
The reason you have an issue with your existing code is that slugify only converts the title field to a slug, not checking for existing slug values. With the method above, unique_slugify will check existing values and generate a slug that does not exist in the db yet.

Related

Django: Remove form fileds based on some values in the database

I want to remove some fields from a form based on some values in the database. I'm not using this form to insert the data into any database, I'm going to make a csv file from this form data. Also this form is not related to any model.
forms.py
class Registration_form(forms.Form):
Applicant_Name = forms.CharField(label='Your name', max_length=100)
Applicant_age = forms.IntegerField(label ='Age of Applicant')
Applicant_email =forms.EmailField(max_length=50)
Applicant_phone = forms.CharField(max_length=10)
views.py
class Registration_View(FormView):
template_name = 'EVENTAPP/Application.html'
form_class = Registration_form
success_url = '/'
def form_valid(self, form):
Applicant_Name = form.cleaned_data['Applicant_Name'],
Applicant_age=form.cleaned_data['Applicant_age'],
Applicant_email=form.cleaned_data['Applicant_email']
Applicant_phone=form.cleaned_data['Applicant_phone']
# do some operations if form data valid
return super().form_valid(form)
models.py
class es_event(models.Model):
ev_name = models.CharField(max_length=100,verbose_name="Event Name")
ev_date = models.DateField(auto_now=False, verbose_name="Date")
ev_description = models.TextField(null=True, verbose_name="Description")
registrant_name = models.BooleanField(default=True )
registrant_age = models.BooleanField(default=False)
registrant_phone = models.BooleanField(default=False)
registrant_email = models.BooleanField(default=False)
registrant_institution = models.BooleanField(default=False)
name = models.CharField(max_length=100,null=True)
reg_open = True
slug = models.SlugField(max_length=250)
def save(self, *args, **kwargs):
self.slug = slugify(self.ev_name)
return super(es_event, self).save(*args, **kwargs)
def get_absolute_url(self):
return reverse('event_detail', kwargs={'id': self.id, 'slug': self.slug })
urls.py
url(r'^events/register(?P<id>\d+)(?:/(?P<slug>[\w\d-]+))?/$', views.Registration_View.as_view(), name='event_application')
Now what I want to do is find a particular instance of es_event from the database by using the value of "id" in the URL.
Then if that instance has the attributes registrant_name,registrant_age, etc is True then the fields Applicant_Name, Applicant_age, etc will be available on the form
You can use AJAX for that. I think this is an example similar to yours, just that instead of checking if the user exists, you check if your instance has desired attributes (registrant_name, registrant_age). And when you get JSON response you show/hide fields with Javascript.

Exclude fields for Django model, only on creation

I am building a notification system for a company, where admin users can create Projects and add users to them. The Project model has 9 attributes but I only want to show 3 or 4 fields when a Project is created, but show them all when an existing Project is updated.
This change will only need to be reflected on the Django admin site, so I have extended the ProjectAdmin with my own ProjectForm, where I extend the init method to check if it is a new instance and if so remove certain fields.
# models.py
class Project(models.Model):
project_number = models.IntegerField()
name = models.CharField(max_length=100)
permit = models.CharField(max_length=100, blank=True, default='')
is_active = models.BooleanField(default=True)
users = models.ManyToManyField(CustomUser, blank=True, related_name='project_users')
# add a default
levels = models.ManyToManyField('Level', blank=True, related_name='project_levels')
total_contract_hours = models.IntegerField(default=0, blank=True, verbose_name='Total Design Hours')
hours_used = models.IntegerField(default=0, blank=True, verbose_name='Total Design Hours Used')
notes = models.ManyToManyField('notes.ProjectNote', related_name='core_project_notes', blank=True)
history = HistoricalRecords()
def __str__(self):
ret_str = "{} {}".format(self.project_number, self.name)
if self.permit:
ret_str += " | Permit: {}".format(self.permit)
return ret_str
# admin.py
class ProjectForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ProjectForm, self).__init__(*args, **kwargs)
attrs = {'class': 'form-control', 'required': True}
if self.instance and self.instance.pk is None:
# creating project
exclude = ['is_active', 'users', 'levels', 'hours_used', 'notes']
for field in exclude:
try:
del self.fields[field]
except ValueError:
print('{} does not exist'.format(field))
for field in self.fields.values():
field.widget.attrs = attrs
class Meta:
model = Project
fields = ['project_number', 'name', 'total_contract_hours']
class ProjectAdmin(admin.ModelAdmin):
form = ProjectForm
fields = ['project_number', 'name', 'permit', 'is_active', 'users', 'levels', 'total_contract_hours', 'hours_used', 'notes']
As I stated I only want basic Project fields on creation, but show all attributed when updating existing Project. With just these changes, I now get a KeyError:
KeyError: "Key 'is_active' not found in 'ProjectForm'. Choices are:
name, permit, project_number, total_contract_hours."
However, when I print the available fields it returns an OrderedDict with all of the model attributes as keys. What am I doing wrong? Thanks!
I figured it out, the field must be in listed in Meta and then you just set the field to be a hidden field.
class ProjectForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ProjectForm, self).__init__(*args, **kwargs)
print("Adding project")
if not self.instance or self.instance.pk is None:
for name, field in self.fields.items():
if name in ['design_manager', ]:
field.widget = forms.HiddenInput()
class Meta:
model = Project
fields = ['project_number', 'name', 'design_manager', 'total_contract_hours']
class ProjectAdmin(admin.ModelAdmin):
form = ProjectForm
def save_model(self, request, obj, form, change):
obj.design_manager = request.user
super().save_model(request, obj, form, change)

How to create or update value to relationship model

I want to save the Portfolio products details in PortfolioProducts model in django
I have models like below:
class Product(models.Model):
name = models.CharField(max_length=255,null=True, verbose_name ='Name')
class Portfolio(models.Model):
name = models.CharField(max_length=100, blank=True, null=True, verbose_name ='Name')
class PortfolioProducts(models.Model):
portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE, verbose_name ='Portfolio')
product = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name ='Product')
Portfolio form:
class PortfolioForm(forms.ModelForm):
class Meta:
model = Portfolio
fields = ['name']
My view file:
def edit(request):
portfolio_form = PortfolioForm
if request.method=="POST":
portfolio_id=request.POST.get('portfolio_id')
portfolio_detail = Portfolio.objects.get(pk=portfolio_id)
pform = portfolio_form(request.POST, instance=portfolio_detail)
if pform.is_valid():
portfolio = pform.save(commit = False)
portfolio.save()
products=request.POST.getlist('product_id[]')
for product in products:
ppform = PortfolioProducts(product_id=product, portfolio_id=portfolio_id)
port_product = ppform.save()
I am trying to save and update the Portfolio products like this, but is adding products to portfolio multiple time.
Well, you don't need to update PortfolioProduct for updating Portofilio. Because even if you update Portfolio, its primary key remains same as before. So the relationship remains the same.
But, in your case, if PortofolioProduct does not exist for a product in products and Portfolio object, then you can create one like this:
for product in products:
ppform, _ = PortfolioProducts.objects.get_or_create(product_id=product, portfolio_id=portfolio_id)
Update
From comments: you need to either remove def save(self): methods from you Model(Because you are not doing anything particular in those save methods) or if intend to keep you save() methods, then you need to call the super properly, like this:
class Product(models.Model):
name = models.CharField(max_length=255,null=True, verbose_name ='Name')
def save(self, *args, **kwargs):
super(Product, self).save(*args, **kwargs)
class Portfolio(models.Model):
name = models.CharField(max_length=100, blank=True, null=True, verbose_name ='Name')
def save(self, *args, **kwargs):
super(Portfolio, self).save(*args, **kwargs)
class PortfolioProducts(models.Model):
portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE, verbose_name ='Portfolio')
product = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name ='Product')
def save(self, *args, **kwargs):
super(PortfolioProducts, self).save(*args, **kwargs)
Yes, I also got stuck with the same issue in my django project. The thing it does in my case was everytime the user tries to update his/her profile, it created a new one, this is because of the Foreign Key to it. I fixed the issue by deleting the previous user profile (in your case it's portfolio) every time the user updates it.
class UserEdit(TemplateView):
template_name = 'accounts/homee.html'
def get(self, request):
form = UserProfilee()
ppp = UserProfile.objects.get(user=request.user)
return render(request, self.template_name, {'form': form, 'ppp': ppp})
def post(self, request):
form = UserProfilee(request.POST, request.FILES)
pppp = UserProfile.objects.get(user=request.user)
if form.is_valid():
post = form.save(commit=False)
post.user = request.user
if not post.image:
post.image = pppp.image
UserProfile.objects.filter(user=post.user).delete()
post.save()
return redirect('/home/homepage/')
args = {'form': form}
return render(request, self.template_name, args)
As you see,I filter the user and delete the user profile whenever user updates his/her profile thus leaving only 1 user profile.

Django modelforms, foreignkey filter for data owned by logged in user [duplicate]

I'm trying to display a form (ModelForm) with a select field filtered by currently logged in user. The select field in this case contains a list of categories. I want to display only the categories which "belong" to the currently logged in user. The category field is a foreign key to the IngredienceCategory model.
Here is what I've come up with so far but it's giving me an error (unexpected keyword queryset). Any ideas what I'm doing wrong?
# models.py
class IngredienceCategory(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredience Categories"
def __unicode__(self):
return self.name
class Ingredience(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
category = models.ForeignKey(IngredienceCategory, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredients"
def __unicode__(self):
return self.name
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
# views.py
def home(request):
if request.user.is_authenticated():
username = request.user.username
email = request.user.email
foods = Food.objects.filter(user=request.user).order_by('name')
ingredients = Ingredience.objects.filter(user=request.user).order_by('name')
ingrcat = IngredienceCategory.objects.filter(user=request.user)
if request.method == 'POST':
form = IngredienceForm(request.POST)
if form.is_valid():
# Create an instance of Ingredience without saving to the database
ingredience = form.save(commit=False)
ingredience.user = request.user
ingredience.save()
else:
# How to display form with 'category' select list filtered by current user?
form = IngredienceForm(queryset=IngredienceCategory.objects.filter(user=request.user))
context = {}
for i in ingredients:
context[i.category.name.lower()] = context.get(i.category.name.lower(), []) + [i]
context2 = {'username': username, 'email': email, 'foods': foods, 'ingrcat': ingrcat, 'form': form,}
context = dict(context.items() + context2.items())
else:
context = {}
return render_to_response('home.html', context, context_instance=RequestContext(request))
That's happening because ModelForm does not take a queryset keyword.
You can probably achieve this by setting the queryset on the view:
form = IngredienceForm()
form.fields["category"].queryset =
IngredienceCategory.objects.filter(user=request.user)
See related question here.
Here i have another suggestion to solve the problem. You can pass request object in your form object inside view.
In view.py just pass the request object.
form = IngredienceForm(request)
In your forms.py __init__ function also add request object
from models import IngredienceCategory as IC
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
def __init__(self, request, *args, **kwargs):
super(IngredienceForm, self).__init__(*args, **kwargs)
self.fields['name'].queryset = IC.objects.filter(user=request.user)
This filter always will be applied whenever you initialize your form .

Filter select field in ModelForm by currently logged in user

I'm trying to display a form (ModelForm) with a select field filtered by currently logged in user. The select field in this case contains a list of categories. I want to display only the categories which "belong" to the currently logged in user. The category field is a foreign key to the IngredienceCategory model.
Here is what I've come up with so far but it's giving me an error (unexpected keyword queryset). Any ideas what I'm doing wrong?
# models.py
class IngredienceCategory(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredience Categories"
def __unicode__(self):
return self.name
class Ingredience(models.Model):
name = models.CharField(max_length=30)
user = models.ForeignKey(User, null=True, blank=True)
category = models.ForeignKey(IngredienceCategory, null=True, blank=True)
class Meta:
verbose_name_plural = "Ingredients"
def __unicode__(self):
return self.name
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
# views.py
def home(request):
if request.user.is_authenticated():
username = request.user.username
email = request.user.email
foods = Food.objects.filter(user=request.user).order_by('name')
ingredients = Ingredience.objects.filter(user=request.user).order_by('name')
ingrcat = IngredienceCategory.objects.filter(user=request.user)
if request.method == 'POST':
form = IngredienceForm(request.POST)
if form.is_valid():
# Create an instance of Ingredience without saving to the database
ingredience = form.save(commit=False)
ingredience.user = request.user
ingredience.save()
else:
# How to display form with 'category' select list filtered by current user?
form = IngredienceForm(queryset=IngredienceCategory.objects.filter(user=request.user))
context = {}
for i in ingredients:
context[i.category.name.lower()] = context.get(i.category.name.lower(), []) + [i]
context2 = {'username': username, 'email': email, 'foods': foods, 'ingrcat': ingrcat, 'form': form,}
context = dict(context.items() + context2.items())
else:
context = {}
return render_to_response('home.html', context, context_instance=RequestContext(request))
That's happening because ModelForm does not take a queryset keyword.
You can probably achieve this by setting the queryset on the view:
form = IngredienceForm()
form.fields["category"].queryset =
IngredienceCategory.objects.filter(user=request.user)
See related question here.
Here i have another suggestion to solve the problem. You can pass request object in your form object inside view.
In view.py just pass the request object.
form = IngredienceForm(request)
In your forms.py __init__ function also add request object
from models import IngredienceCategory as IC
class IngredienceForm(ModelForm):
class Meta:
model = Ingredience
fields = ('name', 'category')
def __init__(self, request, *args, **kwargs):
super(IngredienceForm, self).__init__(*args, **kwargs)
self.fields['name'].queryset = IC.objects.filter(user=request.user)
This filter always will be applied whenever you initialize your form .