Home > front end >  How to prevent Django save action to avoid repetitive DB calls on already existing data
How to prevent Django save action to avoid repetitive DB calls on already existing data

Time:01-08

My current code for saving articles saves new articles and prints 'The Post Already Exists!' if a duplicate is found, but it's still making DB calls and so now I have ID gaps in my articles table because of the false saves caused by duplicate articles not being saved. How can I improve my code to prevent the save action if a duplicate is found to preserve consistency in my article IDs?

if not Posts.objects.filter(title=post_title, slug=post_slug).exists():
   post = Posts(
        title = post_title,
        link = post_link,
        summary = post_summary,
        image_url = image_link,
        slug = post_slug,
        pub_date = date_published,
        guid = item.guid,
        feed_title = channel_feed_title,
        feed_link = channel_feed_link,
        feed_description = channel_feed_desc,
        source_id = selected_source_id,
        category_id = selected_category_id
       )
   post.save()

   for i in range(len(article_list)):
       post_tags = post.tags.add(article_list[i])

else:
   print("The Post Already Exists! Skipping...")

I keeping getting such errors:

django.db.utils.IntegrityError: duplicate key value violates unique constraint "Posts_posts_slug_key" DETAIL:  Key (slug)=() already exists.

My Models

class Categories(models.Model):
    category_name = models.CharField(max_length=500, verbose_name='Categories')
    date_created = models.DateField(auto_now_add=True, verbose_name='Date Created')
    last_modified = models.DateField(auto_now=True, verbose_name='Last Modified')

    def __str__(self):
        return f"{self.category_name}" 

    class Meta:
        verbose_name_plural = 'Categories'

class Source(models.Model):
    name = models.CharField(max_length=500, verbose_name='Website Name')
    feed_url = models.URLField(max_length=500, verbose_name='RSS Feed Link')
    date_created = models.DateField(auto_now_add=True, verbose_name='Date Created')
    last_modified = models.DateField(auto_now=True, verbose_name='Last Modified')

    def __str__(self):
        return f"{self.name}" 

    class Meta:
        verbose_name_plural = 'Feed Sources'

class Posts(models.Model):
    title = models.CharField(max_length=500, verbose_name='Post Title')
    link = models.URLField(max_length=500, verbose_name='Post Link')
    summary = models.TextField(verbose_name='Post Summary')
    image_url = models.URLField(max_length=500, null=True, verbose_name='Post Image URL')
    slug = models.SlugField(unique=True, max_length=500)
    tags = TaggableManager()
    pub_date = models.DateTimeField(verbose_name='Date Published')
    guid = models.CharField(max_length=500, verbose_name='Guid Code', null=True)
    feed_title = models.CharField(max_length=500, verbose_name='Feed Channel Title')
    feed_link = models.URLField(max_length=500, verbose_name='Feed Channel Link')
    feed_description = models.TextField(verbose_name='Feed Channel Description')
    date_created = models.DateField(auto_now_add=True, verbose_name='Date Created')
    last_modified = models.DateField(auto_now=True, verbose_name='Last Modified')
    source = models.ForeignKey(Source, on_delete=models.CASCADE, verbose_name='Source')
    category = models.ForeignKey(Categories, on_delete=models.CASCADE, verbose_name='Category')

    def __str__(self):
        return f"{self.title} - {self.feed_title}" 

    class Meta:
        verbose_name_plural = 'Posts'

CodePudding user response:

Since you only have slug as a unique field, you must check if a post with the same slug already exists. If not, create it. The problem you have now is that you also check the title (as an AND operator). This could lead you to think the post doesn't exist, but you still can't add it to the DB because the slug already exists. But not the slug with a different title. This would mean that you only need to delete the title in your query:

Posts.objects.filter(slug=post_slug).exists()

Here are some more methods to solve the problem based on my earlier answer (https://docs.djangoproject.com/en/4.0/ref/models/querysets/#get-or-create):

try:
    post = Posts.objects.get(slug=post_slug)
except Posts.DoesNotExist:
    post = Posts(title=post_title, link=...)
    post.save()

Here, with concurrent requests, multiple attempts to save a Post with the same parameters may be made. To avoid this race condition, the above example can be rewritten using get_or_create() like so:

obj, created = Posts.objects.get_or_create(
    slug=post_slug,
    defaults={'title':post_title, 'link': ...},
)
  •  Tags:  
  • Related