Django: Key (slug)=(*) already exists - python

I'm pretty new to Django and python and I'd like to learn more about how to populating my Postgres database.
Here is my current model: models.py
from django.template.defaultfilters import slugify
class Skill(models.Model):
name = models.TextField()
slug = models.TextField(unique = True)
def __unicode__(self):
return "%s" % self.name
and my views: views.py
r = r.json()
try:
Skills = r['data']['skills']
except:
pass
for skill in Skills:
skill = Skill.objects.create(name=skill['name'],slug=slugify(skill['name']))
I'm getting the error:
Exception Type: IntegrityError
DETAIL: Key (slug)=(systems-engineering) already exists.
I've been reading a similar post although still haven't been able to solve my problem. objects.create() will shows an error when the object already exists in the database, but I was getting error with the code above. Could "unique = True" be causing the error? and how do you fix this?
Follow up
My problem is simpler than I thought. I was able to run psql interactive terminal and see my data populating. I wasn't able to see it on the admin site because I missed out registering the models on admin.py

When you provide unique=True, the field will be unique throughout the table. Hence, when you try to add a data which is already in DB, it will raise an error. See this official doc for more details

Related

How can I make a field in Django models that concatenates a specific string to each record's id?

I have made a field facility_id in Django models that should concatenate a specific string "ACCTS-" on the left with each record's id on the right,
My model class is below:
class Facility(models.Model):
...
id = models.BigAutoField(primary_key=True)
facility_id = models.CharField(max_length=50, default=print(f'{"ACCTS-"}{id}'), editable=False)
...
I want to the facility_id field to be storing special and readable human friendly facility_id's of the form: ACCTS-1, ACCTS-2, ACCTS-3, ... corresponding to each individual id.
The migrations didn't throw any errors, however When I try to create the records for this table in the Django Admin, am getting an IntegrityError of:
IntegrityError at /admin/ACCTS_CLYCAS/facility/add/
NOT NULL constraint failed: ACCTS_CLYCAS_facility.facility_id
How do I fix this problem, or what could be the easiest way to implement my problem.
The migrations didn't throw any errors, however When I try to create the records for this table in the Django Admin
That makes sense, since you have set the default=None. Indeed, print(…) returns None and only prints the value to the standard output channel (stdout). It will thus not prepend the value of the id with ACCTS.
If the facility_ids are all just the id prefixed with ACCTS-, you can work with a #property instead:
class Facility(models.Model):
id = models.BigAutoField(primary_key=True)
#property
def facility_id(self):
return f'ACCTS-{self.id}'
You can also try using a post save signal.
Add blank = True to facility_id and then use a post save signal to update the value of facility_id.
You can watch this tutorial on how to use Django Signals

AttributeError: module has no attribute

When I try to change function name "random_string" which is used in auth_code (variable in model class) to any other name it shows me the error in the command line: AttributeError: module 'users_data.models' has no attribute 'random_string'
from django.db import models
from django.utils.timezone import now
import random
from django.core.exceptions import ValidationError
def random_string():
return int(random.randint(000000, 999999))
def validate_phone_number(phone):
if len(phone) < 7:
raise ValidationError('Phone number can not be less than 7 digits')
class Users(models.Model):
phone = models.CharField(verbose_name='Phone', max_length=20, validators=
[validate_phone_number])
auth_code = models.IntegerField(verbose_name='Code',
default=random_string)
get_in_date = models.DateTimeField(default=now, blank=False,
editable=False)
I have seen many posts which cover my problem but I didn't find any useful. I would appreciate any help.
The issue lies within your migrations files, as shown in this issue. Basically, when you generated your previous migration files, it was written that the default was value for a field was random_string.
Now, if you change that function name, your current code will work, but because your already-generated migration files use this function, they will raise an error as they cannot find that function anymore.
I dont know if simply updating the files to replace the name within them would be enough. Other solutions would be to reset the migrations (though it might come as a cost).
The link I've provided offers a script to fix that, but I haven't tested it myself

What's the standard way of saving something only if its foreign key exists?

I'm using Python 3.7 and Django . I have the following model, with a foreign key to another model ...
class ArticleStat(models.Model):
objects = ArticleStatManager()
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='articlestats')
...
def save(self, *args, **kwargs):
if self.article.exists():
try:
article_stat = ArticleStat.objects.get(article=self.article, elapsed_time_in_seconds=self.elapsed_time_in_seconds)
self.id = article_stat.id
super().save(*args, **kwargs, update_fields=["hits"])
except ObjectDoesNotExist:
super().save(*args, **kwargs)
I only want to save this if the related foreign key exists, otherwise, I've noticed errors result. What's the standard Django/Python way of doing something like this? I thought I read I could use ".exists()" (Check if an object exists), but instead I get an error
AttributeError: 'Article' object has no attribute 'exists'
Edit: This is the unit test I have to check this ...
id = 1
article = Article.objects.get(pk=id)
self.assertTrue(article, "A pre-condition of this test is that an article exist with id=" + str(id))
articlestat = ArticleStat(article=article, elapsed_time_in_seconds=250, hits=25)
# Delete the article
article.delete()
# Attempt to save ArticleStat
articlestat.save()
If you want to be sure Article exists in ArticleStat's save method you can try to get it from your database and not just test self.article.
Quoting Alex Martelli:
" ... Grace Murray Hopper's famous motto, "It's easier to ask forgiveness than permission", has many useful applications -- in Python, ... "
I think using try .. except .. else is more pythonic and I will do something like that:
from django.db import models
class ArticleStat(models.Model):
...
article = models.ForeignKey(
Article, on_delete=models.CASCADE, related_name='articlestats'
)
def save(self, *args, **kwargs):
try:
article = Article.objects.get(pk=self.article_id)
except Article.DoesNotExist:
pass
else:
try:
article_stat = ArticleStat.objects.get(
article=article,
elapsed_time_in_seconds=self.elapsed_time_in_seconds
)
self.id = article_stat.id
super().save(*args, **kwargs, update_fields=["hits"])
except ArticleStat.DoesNotExist:
super().save(*args, **kwargs)
If you are using a relational database, foreign key constraints will be added automatically post-migration. save method may not need any customization.
class ArticleStat(models.Model):
objects = ArticleStatManager()
article = models.ForeignKey(
Article, on_delete=models.CASCADE, related_name='articlestats'
)
Use the following code to create ArticleStats
from django.db import IntegrityError
try:
ArticleStats.objects.create(article=article, ...)
except IntegrityError:
pass
If article_id is valid, ArticleStats objects get created else IntegrityError is raised.
article = Article.objects.get(id=1)
article.delete()
try:
ArticleStats.objects.create(article=article, ...)
print("article stats is created")
except IntegrityError:
print("article stats is not created")
# Output
article stats is not created
Note: Tested on MySQL v5.7, Django 1.11
article field on your ArticleStat model is not optional. You can't save your ArticleStat object without the ForeignKey to Article
Here is a similar code, item is a ForeignKey to the Item model, and it is required.
class Interaction(TimeStampedModel, models.Model):
...
item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name='interactions')
type = models.IntegerField('Type', choices=TYPE_CHOICES)
...
If I try to save an object of Interaction from the shell without selecting a ForeignKey to the item, I receive an IntegrityError.
~ interaction = Interaction()
~ interaction.save()
~ IntegrityError: null value in column "item_id" violates not-null constraint
You don't need a check self.article.exists(). Django and Database will require that field and will not let you save the object without it.
You should read about ForeignKey field in Django Docs
You can just test the value of the article field. If it's not set, I believe it defaults to None.
if self.article: # Value is set
If you want this ForeignKey field to be optional (which it sounds like you do), you need to set blank=True and null=True on that field. This will allow the field to be blank (in validation) and will set null on the field when it's not there.
As mentioned in the comments below, your database is likely enforcing the fact that the field is required, and refuses to remove the article instance.
As other answers have pointed out, the Article ForeignKey is required on your ArticleStat model and saving will automatically fail without a valid Article instance. The best way to fail gracefully with non-valid input is using Form validation with Django's Forms API. Or if handling serialized data with Django Rest Framework, using a Serializer, which is the Form-like equivalent for JSON data. That way you don't need to overwrite the save method unless you have a specific requirement.
So far nobody has mentioned the correct usage of .exists(). It is a method of a queryset not a model instance, which is why you get the error you mentioned above when trying to apply it to an individual model instance with self.article.exists(). To check the existence of an object, simply use .filter instead of .get. If your Article (pk=1) exists then:
Article.objects.filter(pk=1)
Will return a queryset with one Article in it:
<Queryset: [Article: 1]>
and
Article.objects.filter(pk=1).exists()
Will return True. Whereas if the item does not exist the query will return an empty queryset and .exists() will return False, rather than raising an exception (as attempting to .get() a non-existent object does). This still applies if the pk previously existed and has been deleted.
EDIT: Just noticed that your ArticleStat's on_delete behaviour is currently set to CASCADE. This means that when an Article is deleted, the related ArticleStat is also deleted. So I think you must have been misinterpreting the errors/difficulties you mentioned in reply to #jonah-bishop's answer when trying if self.article:. For a minimal fix, if you still want to keep the ArticleStat after an Article is deleted, change the on_delete keyword to models.SET_NULL, and as per Jonah's answer, add the extra keywords null=True, blank=True:
article = models.ForeignKey(Article, on_delete=models.SET_NULL, related_name='articlestats', null=True, blank=True)
Then there should be no problem simply doing if self.article: to check for a valid ForeignKey object relation. But using Forms/Serializers is still better practice though.
The code of last line articlestat.save() will fail if the article instance has been deleted. Django and database will check the article automatically for you if you are using relation database like mysql or sqlite3.
During the migrations, a constraint will be created. For example:
shell>>> python manage.py sqlmigrate <appname> 0001
CREATE TABLE impress_impress ...
...
ALTER TABLE `impress_impress` ADD CONSTRAINT
`impress_impress_target_id_73acd523_fk_account_myuser_id` FOREIGN KEY (`target_id`)
REFERENCES `account_myuser` (`id`);
...
So if you want to save the articlestat without article, an error will be raised.
You can call .full_clean() before .save()
from django.core.exceptions import ValidationError
class ArticleStat(models.Model):
#...
def save(self, *args, **kwargs):
try:
self.full_clean()
except ValidationError as e:
# dont save
# Do something based on the errors contained in e.message_dict.
# Display them to a user, or handle them programmatically.
pass
else:
super().save(*args, **kwargs)

Querying model data in form for cleaned DB

So I have forms.py which is as below:
from django import forms
from .models import SipExtension
from xmpp.models import xmpp_buddy_groups
class ExtensionForm(forms.Form):
xmpp_buddy_groups_choices = xmpp_buddy_groups.objects.values_list('group_name',flat=True)
boolean_choices=(('Yes','Yes'),('No','No'))
sip_extension = forms.IntegerField(min_value=0,max_value=100000)
sip_secret = forms.CharField(required=True,max_length=32)
commlink_push = forms.ChoiceField(choices=boolean_choices,widget=forms.CheckboxSelectMultiple,required=True)
real_name = forms.CharField(required=True,max_length=32)
xmpp = forms.ChoiceField(choices=boolean_choices,widget=forms.CheckboxSelectMultiple,required=True)
xmpp_username = forms.CharField(required = True,min_length=5)
xmpp_password = forms.CharField(max_length=32, widget=forms.PasswordInput)
xmpp_buddy_groups_names = forms.MultipleChoiceField(choices=xmpp_buddy_groups_choices,widget=forms.CheckboxSelectMultiple,required=False)
It works fine if my DB is already created by previous migrations. But I faced problem when my DB is blank. To test,I dropped all the tables and then run make migrations and got below error:
django.db.utils.ProgrammingError: relation "extensions_sipextension" does not exist
LINE 1: ...p_buddy_groups_names"."xmpp_buddy_groups_id" FROM "extension...
I am getting problems in handling this on blank database when I need to deploy on entirely new system. I could handle that by commenting the urls which are executing views which needs this form but that is a bad and temporary work around. How to fix this?
I think the issue is with xmpp_buddy_groups_choices attribute in the form. The queryset is evaluated at time of project load. So when you are using ./manage.py makemigrations, the xmpp_buddy_groups_choices trying to evaluate the queryset, but failed as there is no table in database. Try to wrap the choices in a function and pass that function in the choices parameter.
And your choices is required to be a list of tuples (with 2 elements), but the xmpp_buddy_groups_choices is a list of strings.
Sample Code:
class ExtensionForm(forms.Form):
#staticmethod
def get_group_choices():
xmpp_buddy_groups_choices = xmpp_buddy_groups.objects.values_list('group_name',flat=True)
return xmpp_buddy_groups_choices
boolean_choices=(('Yes','Yes'),('No','No'))
sip_extension = forms.IntegerField(min_value=0,max_value=100000)
sip_secret = forms.CharField(required=True,max_length=32)
commlink_push = forms.ChoiceField(choices=boolean_choices,widget=forms.CheckboxSelectMultiple,required=True)
real_name = forms.CharField(required=True,max_length=32)
xmpp = forms.ChoiceField(choices=boolean_choices,widget=forms.CheckboxSelectMultiple,required=True)
xmpp_username = forms.CharField(required = True,min_length=5)
xmpp_password = forms.CharField(max_length=32, widget=forms.PasswordInput)
xmpp_buddy_groups_names = forms.MultipleChoiceField(choices=ExtensionForm.get_group_choices,widget=forms.CheckboxSelectMultiple,required=False)

How to retrieve a single record using icontains in django

I'm trying to query a single record using the following but I get a 500 error.
cdtools = CdTool.objects.get(product=product_record.id,
platform=platform_record.id, url__icontains=toolname)
models.py
class CdTool(models.Model):
product = models.ForeignKey(Product)
platform = models.ForeignKey(Platform)
url = models.CharField(max_length=300)
a model instance has an full url, but in my query 'toolname' is the short form (ie: google instead of https://google.com), so I'm trying to figure out what the correct query would be here. I'm open to modifying models.py as long as there is no data migration stuff involved.

Categories