update_or_create not updating/creating objects - django

I found out that django 1.7 supports now update_or_create() method in queryset.
But when I try it, it is not working. There is no object created when I try this:
models.py
class Model1(models.Model):
name = models.CharField(max_length=100)
status = models.CharField(max_length=100)
user = models.ForeignKey(User, null=True, blank=True)
class Model2(models.Model)
model = models.ForeignKey(Model1, null=True, blank=True)
field1 = models.CharField(max_length=100)
field2 = models.CharField(max_length=100)
user = models.ForeignKey(User, null=True, blank=True)
views.py
def ModelUpdate(request)
model1 = get_object_or_404(Model1, pk=request.POST['lid'])
model1.status =2
model1.save()
#based on the model1, we want to create or update model2
datadict ={'field1' : 1, 'field2':2,}
model2, created = Model2.objects.update_or_create(model=model1,
defaults=datadict)
if created:
print('model2 obj created') #for checking purpose
else:
print('model2 obj not created')
return render(request,'updated.html', {'update':'updated'})
I can see the message model2 obj created but the object was not saved in the database. What could be the problem?
UPDATE
Turns out it was my mistake. In the Model2, I defined the save method without returning super(Model2, self).save(*args, **kwargs)
That's why it didn't save the objects.

Maybe you verifying in the wrong way, because I created project with your models and everything works fine.
In my case I'm verifying that object was created/updated with objects.get() and printing values:
In [1]: from app1 import models
In [2]: m1 = models.Model1.objects.create(name=1, status=1)
In [3]: m1
Out[3]: <Model1: Model1 object>
In [4]: datadict ={'field1' : 1, 'field2':2,}
In [6]: model2, created = models.Model2.objects.update_or_create(model=m1, defaults=datadict)
In [7]: model2, created
Out[7]: (<Model2: Model2 object>, True)
In [8]: models.Model2.objects.get().field1, models.Model2.objects.get().field2
Out[8]: (u'1', u'2')
In [9]: datadict ={'field1' : 3, 'field2':4}
In [10]: model2, created = models.Model2.objects.update_or_create(model=m1, defaults=datadict)
In [11]: model2, created
Out[11]: (<Model2: Model2 object>, False)
In [12]: models.Model2.objects.get().field1, models.Model2.objects.get().field2
Out[12]: (u'3', u'4')
and
In [13]: models.Model2.objects.get().model.id
Out[13]: 1
In [14]: models.Model1.objects.get().id
Out[14]: 1

Related

How to turn off reverse relation within same model in django

I have the following django model
class Course(models.Model):
name = models.CharField(max_length=50)
pre_req_courses = models.ManyToManyField('self')
def __str__(self):
return self.name
when I create courses in following way:
course1 = Course.objects.create(name='Course1')
course1.save()
course2 = Course.objects.create(name='Course2')
course2.save()
course2.pre_req_courses.set(course1)
when I run the following command I get:
course2.pre_req_courses.all()
>>> <QuerySet [<Course: Course1>]>
course1.pre_req_courses.all()
>>> <QuerySet [<Course: Course2>]>
Wht I want is:
course2.pre_req_courses.all()
>>> <QuerySet [<Course: Course1>]>
course1.pre_req_courses.all()
>>> <QuerySet []>
How can I achieve this
I couldn't solve it with the m2m field.
It's a little complicated, but you can get the result you want with the code below.
models.py
from django.db import models
class CourseRelation(models.Model):
low_level = models.ForeignKey(
'course.Course',
models.CASCADE,
related_name='relations_me_low'
)
high_level = models.ForeignKey(
'course.Course',
models.CASCADE,
related_name='relations_me_high'
)
def __str__(self):
return '%s -> %s' % (self.low_level, self.high_level)
class Course(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
#property
def pre_req_courses(self):
course_ids = CourseRelation.objects.filter(high_level=self).values('low_level')
return Course.objects.filter(id__in=course_ids)
#property
def next_courses(self):
course_ids = CourseRelation.objects.filter(low_level=self).values('high_level')
return Course.objects.filter(id__in=course_ids)
shell
>>> python_course = Course.objects.create(name='Python')
>>> web_course = Course.objects.create(name='Web')
>>> django_course = Course.objects.create(name='Django')
>>> CourseRelation.objects.create(low_level=python_course, high_level=django_course)
<CourseRelation: Python -> Django>
>>> CourseRelation.objects.create(low_level=web_course, high_level=django_course)
<CourseRelation: Web -> Django>
>>> python_course.pre_req_courses
<QuerySet []>
>>> python_course.next_courses
<QuerySet [<Course: Django>]>
>>> django_course.pre_req_courses
<QuerySet [<Course: Python>, <Course: Web>]>
>>> django_course.next_courses
<QuerySet []>
update
When I read Abdul Aziz Barkat's comment, I realized that a single line would solve it.
pre_req_courses = models.ManyToManyField(
'self',
related_name='next_courses',
symmetrical=False
)

How to create a dataframe from a model (Django)

I am developing an app in Django.
I have this model
class my_model(models.Model):
Field_A = models.CharField(max_length=256, blank=True, null=True)
Field_B = models.CharField(max_length=25, blank=True, null=True)
Field_C = models.TextField(blank=True, null=True)
I want to create a dataframe with columns names equal to model fields names,
containing in every row the model objects, and in every column the object fields values.
How can I do it? Is there a single command to do it? Or do I have to iterate?
EDIT: Here is the raw and unelegant solution that I found until now:
import pandas as pd
entries = my_model.objects.all()
# this generates an array containing the names of the model fields
columns_names = [field.name for field in my_model._meta.get_fields()]
L_GI = len(entries)
# generate empty dataframe
GI = pd.DataFrame(columns = columns_names)
for element in entries:
new_entry = {"Field_A":element.Field_A, "Field_B":element.Field_B, "Field_C":element.Field_C}
GI = GI.append(new_entry, ignore_index=True)
I bet there is a faster way that avoids iteration. Any suggestions?
Nice question
I think you are forced to iterate them.
I have implemented it in 3 different ways so that you can choose your favorite one
import time
import pandas as pd
from django.core import serializers
class PandasModelMixin(models.Model):
class Meta:
abstract = True
#classmethod
def as_dataframe(cls, queryset=None, field_list=None):
t1 = time.time()
if queryset is None:
queryset = cls.objects.all()
if field_list is None:
field_list = [_field.name for _field in cls._meta._get_fields(reverse=False)]
data = []
[data.append([obj.serializable_value(column) for column in field_list]) for obj in queryset]
columns = field_list
df = pd.DataFrame(data, columns=columns)
print("Execution time without serialization: %s" % time.time()-t1)
return df
#classmethod
def as_dataframe_using_django_serializer(cls, queryset=None):
t1 = time.time()
if queryset is None:
queryset = cls.objects.all()
if queryset.exists():
serialized_models = serializers.serialize(format='python', queryset=queryset)
serialized_objects = [s['fields'] for s in serialized_models]
data = [x.values() for x in serialized_objects]
columns = serialized_objects[0].keys()
df = pd.DataFrame(data, columns=columns)
df = pd.DataFrame()
print("Execution time using Django serializer: %s" % time.time()-t1)
return df
#classmethod
def as_dataframe_using_drf_serializer(cls, queryset=None, drf_serializer=None, field_list=None):
from rest_framework import serializers
t1 = time.time()
if queryset is None:
queryset = cls.objects.all()
if drf_serializer is None:
class CustomModelSerializer(serializers.ModelSerializer):
class Meta:
model = cls
fields = field_list or '__all__'
drf_serializer = CustomModelSerializer
serialized_objects = drf_serializer(queryset, many=True).data
data = [x.values() for x in serialized_objects]
columns = drf_serializer().get_fields().keys()
df = pd.DataFrame(data, columns=columns)
print("Execution time using DjangoRestFramework serializer: %s" % time.time()-t1)
return df
So inherit your Model in this way:
class MyModel(PandasModelMixin):
field_a = models.CharField(max_length=256, blank=True, null=True)
field_b = models.CharField(max_length=25, blank=True, null=True)
field_c = models.TextField(blank=True, null=True)
and try the code in this way:
>> MyModel.as_dataframe()
>> MyModel.as_dataframe_using_django_serializer()
>> MyModel.as_dataframe_using_drf_serializer()
I have tried my code using a Model with 450 instances and 15 columns and I had these results:
Execution time without serialization: 0.07040905952453613
Execution time using Django serializer: 0.07644820213317871
Execution time using DjangoRestFramework serializer: 0.12314629554748535
N.B.
I'm using Django 2.2 and Python 3.6.5

How to check if data already exists in the table using django

I am new with django framework struggling to compare value from the database.
this are my tables in models.py :
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE,)
title = models.CharField(max_length=200)
content = models.TextField()
creationDate = models.DateTimeField(auto_now_add=True)
lastEditDate = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
class Votes(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE,)
post_id = models.ForeignKey(Post, on_delete=models.CASCADE,)
up_vote = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(1)])
down_vote = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(1)])
class Meta:
unique_together = (("user","post_id"),)
I have data in the vote tabe like this:
Now what I want is to check in the above table if 'user_id' and 'post_id' already exists in the Votes tabel's rows if the exist throw a message if not add value on upvote or downvote, i gues everyone understand what i want if not please let me know.
something which i tried was this code:
def chk_table():
user_id = request.user
post_id = id
votes_table = Votes.objects.filter(user_id=user_id, post_id= post_id).exists()
return votes_table
but this function is checking in hole table not just in just in one row...
Assuming that, in your urls.py
from django.urls import path
from .views import add_vote
urlpatterns = [
path('post/<int:post_id>/vote/add/', add_vote, name='add-vote'),
]
In your views.py
from django.shortcuts import redirect, render
def add_vote(request, post_id):
if request.method == 'POST':
# receive your POST data here
user_id = request.user.id
post_id = post_id
if not Votes.objects.filter(user_id=user_id, post_id=post_id).exists():
Votes.objects.create(**your_data)
redirect('your-desired-url')
else:
# your logic here
I see you already defined unique_together in Meta so you can use try except
from django.db import IntegrityError
try:
# your model create or update code here
except IntegrityError as e:
if 'unique constraint' in e.message:
# duplicate detected

Data not getting inserted in db in django

Hi I am trying to select data from a dropdown list and then save it in Category model.There is no problem in retrieving the data but when i check it using Category.objects.all(), I get this
<QuerySet [<Category: Category object>, <Category: Category object>, <Category: Category object>, <Category: Category object>]>
models.py:
from __future__ import unicode_literals
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
user = models.ForeignKey(User)
category= models.CharField(max_length=100)
views.py:
def get_category(request):
cname = request.POST.get("dropdown1")
user = request.session.get('user')
obj=Category(user_id=user,category=cname)
obj.save()
return HttpResponse("Registeration succesfull")
With get_category i am trying to save entry selected from dropdown.It works fine but i guess nothing is being stored in the db.
I tried running this
Category.objects.get(category = "abc")
I get this error:
DoesNotExist: Category matching query does not exist.
Can someone tell if this is not the right way to insert data in db.
you are getting category object in Category.objects.all() because you have not specified any string or unicode name for the instances.
in your model append these
class Category(models.Model):
user = models.ForeignKey(User)
category= models.CharField(max_length=100)
def __str__(self):
return self.category
you can also replace __str__ with __unicode__
now the category names will be visible in the queryset.

get extra field value in Django's many-to-many relationship

In Django's many-to-many relationships extra fields, we can add extra fields, as the code below (from https://docs.djangoproject.com/en/dev/topics/db/models/#intermediary-manytomany), my question is how could I get the extra field easily, is it possible to not query on the intermediary table directly? the exactly example is:
>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(person=ringo, group=beatles,
... date_joined=date(1962, 8, 16),
... invite_reason="Needed a new drummer.")
>>> m1.save()
>>> beatles.members.all()
[<Person: Ringo Starr>]
>>> p = beatles.members.all()[0]
>>> p
[<Person: Ringo Starr>]
I want to get the date_joined value of p, I know it can be done by
>>> Membership.objects.get(person=p, group=beatles).date_joined
datetime.date(1962, 8, 16)
but is it possible to get it simply just like:
>>> p.xxx(*args)
=============== models.py ================
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self): # __unicode__ on Python 2
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self): # __unicode__ on Python 2
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
From the documentation page you link to:
Another way to access the same information is by querying the many-to-many reverse relationship from a Person object:
>>> ringos_membership = ringo.membership_set.get(group=beatles)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'