Django 2: Can't import models and save it - python

I somehow can not import my models from another app. I already looked it up and tried but does not work.
events/models.py
class Registration(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
team = models.CharField(max_length=50, blank=True)
date_created = models.DateTimeField(auto_now_add=True)
def save(self, *args, **kwargs):
payment = Payment.objects.create(registration=self)
super().save(*args, **kwargs)
When I run python manage.py makemigrations I get this.
Error
File "C:\Users\Rubber\ems\events\models.py", line 5, in <module>
from payments.models import Payment
File "C:\Users\Rubber\ems\payments\models.py", line 6, in <module>
from events.models import Registration
ImportError: cannot import name 'Registration'
payments/models.py
import uuid
from django.db import models
from django.utils import timezone
from events.models import Registration # THIS WONT WORK
def _create_uuid():
return uuid.uuid1()
def _get_uuid():
return _create_uuid()
class Payment(models.Model):
uuid = models.CharField(max_length=1, default=_get_uuid)
paid = models.BooleanField(default=False)
registration = models.ForeignKey(Registration, on_delete=models.CASCADE)
What am I doing wrong?

You have a circular import. In this case, you can avoid it by removing the Registration import and using the string instead:
class Payment(models.Model):
registration = models.ForeignKey('events.Registration', on_delete=models.CASCADE)
Another option would be to move the Payment.objects.create() code into a signal. Note that your current save() method creates a Payment every time the registration is saved, which might not be what you want.

Related

Creating django api view that returns filtered objects

Problem Description
I'm new to django-rest-framework and got a problem in my view. I have three models namely #User, #Profession and #StudentProfessions. I need to create an api-view that takes user_id as request-parameter and returns a list of all professions that belongs to a particular user_id.
Here's My Codes
profession_app >> models.py
from django.db import models
class Profession(models.Model):
profession_name = models.CharField(max_length=100)
def __str__(self):
return self.profession_name
User Model
I used the django default model.
student_profile_app >> models.py
from django.contrib.auth.models import User
from department_app.models import Department
from profession_app.models import Profession
from program_app.models import Program
from django.db import models
class StudentProfile(models.Model):
student_status = models.BooleanField()
phone_number = models.CharField(max_length=100)
year_of_study = models.CharField(max_length=100)
program_id = models.ForeignKey(Program, on_delete=models.CASCADE)
student_id = models.OneToOneField(User, on_delete=models.CASCADE, related_name="student_id")
organization_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name="organization_id")
profile_image = models.ImageField(upload_to='images/', blank=True)
field_supervisor_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name="field_supervisor")
department_id = models.ForeignKey(Department, on_delete=models.CASCADE)
academic_supervisor_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name="academic_supervisor")
def __str__(self):
return f'{self.student_id.username } Profile'
class StudentProfession(models.Model):
student_id = models.ForeignKey(User, on_delete=models.CASCADE, related_name="student_profession_id")
profession_id = models.ForeignKey(Profession, on_delete=models.CASCADE)
def __str__(self):
return f'{self.student_id.username } Profession'
student_profile_app >> views.py
from .models import StudentProfile, StudentProfession
def getStudentProfessions(request, studentId):
professions = StudentProfession.objects.filter(student_id=studentId)
return professions
And In My urls
from student_profile_app.views import getStudentProfessions
from rest_framework import routers
router.register('getStudentProfessions/<int:studentId>', getStudentProfessions, 'getStudentProfessions')
urlpatterns = router.urls
But when i run the server i got the following error
urlpatterns = router.urls
File "E:\Codes\FIP\fipEnv\lib\site-packages\rest_framework\routers.py", line 77, in urls
self._urls = self.get_urls()
File "E:\Codes\FIP\fipEnv\lib\site-packages\rest_framework\routers.py", line 338, in get_urls
urls = super().get_urls()
File "E:\Codes\FIP\fipEnv\lib\site-packages\rest_framework\routers.py", line 236, in get_urls
routes = self.get_routes(viewset)
File "E:\Codes\FIP\fipEnv\lib\site-packages\rest_framework\routers.py", line 152, in get_routes
extra_actions = viewset.get_extra_actions()
AttributeError: 'function' object has no attribute 'get_extra_actions'
First of all StudentProfession.student_id has bad model parameter set (its related to User model - it should be Student model).
Django rest framework uses viewset's for routers.
What you need is serializer which will represent your endpoint api structure and viewset.
I will write simple serializer and viewset for you but you really need to read docs.
After edits from comments:
Serializer class:
from rest_framework import serializers
#...other imports
class StudentProfessionSerializer(serializers.ModelSerializer):
class Meta:
model = StuedentProfession
fields = ('profession')
Viewset class (that's what you register in router !)
from rest_framework import viewsets, mixins
#...other imports
class StudentProfessionViewSet(viewsets.GenericViewSet,
mixins.ListModelMixin,
mixins.RetrieveModelMixin):
serializer_class = StudentProfessionSerializer
queryset = StudentProfession.objects
def get_queryset(self):
student_id = self.kwargs.get('studentId')
return self.queryset.filter(student_id=student_id)
Some tips from me:
READ DOCS
you dont need to write "_id" suffix in ForeignKey fields (django make it automatically underhood - means that columns in your table will have _id suffix anyway) and then you can use params without this ugly _id... like this
StudentProfession.objects.filter(student=somestudentid)
your API should be constructed like
router.register(r'students/(?P<studentId>\d+)/professions', StudentProfessionViewSet, 'student-profession')
try not to use "real" ID's of objects in url - use UUID's instead - its safer

Save multiple values in one field (Django)

The problem:
I have a model, which is referencing the basic User model of django. Right now, if I submit the form Django updates my database by replacing the existing data with the new one. I want to be able to access both of them. (In weight and date field)
Models file:
I saw other posts here, where they solved a problem by specifying a foreign key, but that doesn't solve it for me.
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
# Create your models here.
class Profile(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
weight = models.FloatField(max_length=20, blank=True, null=True)
height = models.FloatField(max_length=20, blank=True, null=True)
date = models.DateField(auto_now_add=True)
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
Views file:
This is where I save the data that I get from my form called WeightForm
from django.shortcuts import render
from django.contrib.auth.models import User
from django.http import JsonResponse
from django.shortcuts import get_object_or_404
from users import models
from users.models import Profile
from .forms import WeightForm
def home(request):
form = WeightForm()
if request.is_ajax():
profile = get_object_or_404(Profile, id = request.user.id)
form = WeightForm(request.POST, instance=profile)
if form.is_valid():
form.save()
return JsonResponse({
'msg': 'Success'
})
return render(request, 'Landing/index.html',{'form':form})
What I tried:
I used to have a OneToOneField relation with this model, but as you can see I changed it to foreignkey, according to answers I saw on this site.
Thanks if you've gotten this far in my mess :D
I didn't understood exactly what you mean by "I want to be able to access both of them. (In weight and date field)" but I guess you want user to be able to see their previous data of weight and Date also, so you can try doing this:
In your models.py do try doing this,
class Profile(models.Model):
user_id = models.AutoField(primary_key=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
height = models.FloatField(max_length=20, blank=True, null=True)
def __str__(self):
return self.user.username
class UserData(models.Model):
Data_id = models.AutoField(primary_key=True)
user_id = models.ForeignKey(Profile, on_delete=models.CASCADE)
weight = models.FloatField(max_length=20, blank=True, null=True)
date = models.DateField(auto_now_add=True)
then u can have seperate forms for both the models and use them combined.
You can make a workaround
Create new model which would include something like "version"
Reference to version with foreign key
class ProfileChange(models.Model):
Date = models.DateField(default=datetime.datetime.today().strftime('%Y-%m-%d'))
#classmethod
def create(cls):
object = cls()
return object
class Profile(models.Model):
version = models.ForeignKey(ProfileChange,on_delete=models.CASCADE)
Unfortunately, you could see only one ProfileChange a day. If you want to see more of them, instead of models.DataField use models.IntegerField

Create instance of foreign key object every time parent object is created

I want to create a Cart object that is linked to the User model in my database. Every time I create a User, I want a linked Cart to be created (in the database).
Any suggestions? I tried linking it to the User id but no joy.
models.py
from django.db import models
from django.contrib.auth.models import User
from django.dispatch import receiver
from django.db.models.signals import post_save
# Create your models here.
class Product(models.Model):
name = models.CharField(max_length=50)
desc = models.CharField(max_length=50)
img = models.FileField()
created = models.DateField()
num_stars = models.IntegerField()
class Cart(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE,to_field='id', null=True)
content = models.CharField(max_length=50)
last_update = models.DateTimeField()
Just put receiver to handle creation of cart when user is created
from django.dispatch import receiver
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.utils import timezone
class Cart(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE,to_field='id', null=True)
content = models.CharField(max_length=50,null=True,blank=True)
last_update = models.DateTimeField(default=timezone.now)
#receiver(post_save, sender=User)
def create_user_cart(sender, instance, created, **kwargs):
if created:
Cart.objects.get_or_create(user=instance)

Can't import serializer from other serializer in django rest-framework?

Problem
I have 2 models, leads and notes. I want a lead to be able to have 1 or more notes. I have used a generic foreign key because I want to plan for the future and a note could be assigned to say a person or a meeting for example.
Following the instructions for django rest framework and Rest Framework Generic Relations I am trying to import one serializer from the other to make a reverse relation possible.
Error
I can't import the serializers in both files(call one serializer from the other) because I get:
File "/Users/james/Documents/UtilityCRM-Server/crm/leads/urls.py", line 2, in <module>
from leads import views
File "/Users/james/Documents/UtilityCRM-Server/crm/leads/views.py", line 11, in <module>
from leads.serializers import LeadSerializer
File "/Users/james/Documents/UtilityCRM-Server/crm/leads/serializers.py", line 4, in <module>
from notes.serializers import NoteSerializer
File "/Users/james/Documents/UtilityCRM-Server/crm/notes/serializers.py", line 6, in <module>
from leads.serializers import LeadSerializer
ImportError: cannot import name LeadSerializer
Its weird because if I open the django shell and run the following it lets me import them all:
from leads.serializers import LeadSerializer
from notes.serializers import NotesSerializer
from callbacks.serializers import CallbackSerializer
Any help would be greatly appreciated!
Code
This is my installed app section of my settings file:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# 3rd Party Apps
'rest_framework',
'generic_relations',
# My Apps
'leads.apps.LeadsConfig',
'callbacks.apps.CallbacksConfig',
'notes.apps.NotesConfig',
]
notes/models.py
from __future__ import unicode_literals
from django.db import models
from django.utils import timezone
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
class Note(models.Model):
author = models.ForeignKey('auth.User')
title = models.CharField(max_length=100)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
# Relations
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
note_object = GenericForeignKey('content_type', 'object_id')
def __str__(self):
return self.title
leads/models.py
from __future__ import unicode_literals
from django.db import models
from django.contrib.contenttypes.fields import GenericRelation
from django.utils import timezone
from notes.models import Note
from callbacks.models import Callback
GAS = 'G'
ELECTRICITY = 'E'
LEAD_TYPE_CHOICES = (
(GAS, 'Gas'),
(ELECTRICITY, 'Electricity'),
)
# Create your models here.
class Lead(models.Model):
author = models.ForeignKey('auth.User')
type = models.CharField(
max_length=1,
choices=LEAD_TYPE_CHOICES,
default=GAS,
)
business_registration_number = models.IntegerField(max_length=20)
business_name = models.CharField(max_length=50)
mpan = models.IntegerField(max_length=21)
supplier = models.CharField(max_length=45)
contract_length = models.IntegerField(max_length=2)
contract_start_date = models.DateField()
contract_end_date = models.DateField()
address_line_1 = models.CharField(max_length=45)
address_line_2 = models.CharField(max_length=45)
address_line_3 = models.CharField(max_length=45)
address_city = models.CharField(max_length=45)
address_county = models.CharField(max_length=45)
address_postcode = models.CharField(max_length=10)
contact_title = models.CharField(max_length=45)
contact_first_name = models.CharField(max_length=45)
contact_middle_name = models.CharField(max_length=45)
contact_last_name = models.CharField(max_length=45)
contact_telephone = models.IntegerField(max_length=11)
contact_email = models.EmailField(max_length=60)
created_date = models.DateTimeField(default=timezone.now)
# Relations
assigned_to = models.ForeignKey('auth.User', related_name='+')
#from_batch = models.ForeignKey('data_batch.DataBatch', related_name='+')
#callbacks = GenericRelation(Callback)
notes = GenericRelation(Note)
class Meta:
ordering = ('contract_end_date', 'business_name',)
def __str__(self):
return self.business_name
I have 2 serializers:
leads/serializers.py
from rest_framework import serializers
from leads.models import Lead, LEAD_TYPE_CHOICES
from notes.serializers import NoteSerializer
class LeadSerializer(serializers.ModelSerializer):
notes = NoteSerializer(many=True, read_only=True)
class Meta:
model = Lead
fields = (
'id',
'business_name',
'business_registration_number',
'supplier',
'contract_length',
'contract_start_date',
'notes'
)
notes/serializers.py
from generic_relations.relations import GenericRelatedField
from rest_framework import serializers
from notes.models import Note
from leads.models import Lead
from leads.serializers import LeadSerializer
from callbacks.models import Callback
from callbacks.serializers import CallbackSerializer
class NoteSerializer(serializers.ModelSerializer):
"""
A `Note` serializer with a `GenericRelatedField` mapping all possible
models to their respective serializers.
"""
note_object = GenericRelatedField({
Lead: LeadSerializer(),
Callback: CallbackSerializer()
})
class Meta:
model = Note
fields = (
'id',
'author',
'title',
'text',
'created_date',
'note_object',
)
As I have mentioned previously in a comment, I believe this happens due to circular (cyclic) imports in Python.
This happens particularly when you are declaring related fields in models, and some models have not been instanced yet.
In this case, when you execute your program, it tries to import LeadSerializer, that requires importing NoteSerializer, that requires importing LeadSerializer, that requires importing NoteSerializer... see where this is going?
Your stacktrace says it all:
from leads.serializers import LeadSerializer
from notes.serializers import NoteSerializer
from leads.serializers import LeadSerializer
Generating ImportError: cannot import name LeadSerializer
What I have done to solve this was declaring all the serializers in a single file. Therefore, you have two options:
Move LeadSerializer to notes/serializers.py
Move NoteSerializer to leads/serializers.py
It is not the most elegant way to solve this, but it has done its trick.
The section below provides no further explanation on how to solve this problem, but an observation regarding this issue.
Perhaps Django & DRF could futurely provide methods to avoid this, such as declaring the serializers as
note_object = GenericRelatedField({
Lead: 'leads.serializers'.LeadSerializer,
Callback: CallbackSerializer()
})
or
notes = 'notes.serializers'.NoteSerializer(many=True, read_only=True)
Faced with this and made this thing:
notes = serializers.SerializerMethodField()
def get_notes(self, obj):
from leads.serializers import LeadSerializer
return LeadSerializer(<your_queryset>, many=True/False, read_only=True/False).data
There is an idea to tackle this before I have also got this same error here I will explain to you how I resolve this.
Put your apps inside the project directory
project
-project
-appname1
-models.py
-serilizer.py
-appname2
-models.py
-serilizer.py
-settings.py
in settings.py
INSTALLED_APPS = ['project.appname1', 'project.appname2']
then try to import appname1 serializers into appname2
like this
from project.appname1.serializers import( ArtistSerializer, ArtisTokenSerilizer, ProfessionSerilizer, FollowersSerializer,
FollowingSerializer, ChatMessageSerializer, SendMessageSerializer, ConversationMessageSerializer,
ProjectTypeSerializer)

Circular module dependencies in Python/Django with split-up models

I understand how to break up models, and I understand why circular module dependencies blow things up, but I've run across a problem where breaking up a model into separate files appears to be causing circular dependencies. Here's an exerpt from the code, and I'll follow it up with the traceback from the failing process:
elearning/tasks.py
from celery.task import task
#task
def decompress(pk):
from elearning.models import Elearning
Elearning.objects.get(pk=pk).decompress()
elearning/models.py
from competency.models import CompetencyProduct
from core.helpers import ugc_elearning
from elearning.fields import ArchiveFileField
class Elearning(CompetencyProduct):
archive = ArchiveFileField(upload_to=ugc_elearning)
def decompress(self):
import zipfile
src = self.archive.path
dst = src.replace(".zip","")
print "Decompressing %s to %s" % (src, dst)
zipfile.ZipFile(src).extractall(dst)
ecom/models/products.py
from django.db import models
from django.utils.translation import ugettext_lazy as _
from core.models import Slugable, Unique
from django_factory.models import Factory
from core.helpers import ugc_photos
class Product(Slugable, Unique, Factory):
photo = models.ImageField(upload_to=ugc_photos, width_field="photo_width", height_field="photo_height", blank=True)
photo_width = models.PositiveIntegerField(blank=True, null=True, default=0)
photo_height = models.PositiveIntegerField(blank=True, null=True, default=0)
description = models.TextField()
price = models.DecimalField(max_digits=16, decimal_places=2)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
ecom/models/__init__.py
from django.contrib.auth.models import User
from django.db import models
from ecom.models.products import Product, Credit, Subscription
from ecom.models.permissions import Permission
from ecom.models.transactions import Transaction, DebitTransaction, CreditTransaction, AwardTransaction, FinancialTransaction, PermissionTransaction, BundleTransaction
competency/models.py
from django.db import models
from django.utils.translation import ugettext_lazy as _
from core.models import Slugable, Unique
from ecom.models import Product
from rating.models import Rated
from trainer.models import Trainer
class Competency(Slugable, Unique):
class Meta:
verbose_name = _("Competency")
verbose_name_plural = _("Competencies")
description = models.TextField()
class CompetencyProduct(Product, Rated):
class Meta:
verbose_name = _("Product")
verbose_name_plural = _("Products")
release = models.DateField(auto_now_add=True)
trainers = models.ManyToManyField(Trainer)
competencies = models.ManyToManyField(Competency, related_name="provided_by")
requirements = models.ManyToManyField(Competency, related_name="required_for", blank=True, null=True)
forsale = models.BooleanField("For Sale", default=True)
ecom/models/permissions.py
from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import ugettext_lazy as _
from treebeard.mp_tree import MP_Node
from collective.models import Collective
from course.models import Course
from ecom.models.products import Product
class Permission(MP_Node):
class Meta:
app_label = "ecom"
product = models.ForeignKey(Product, related_name="permissions")
user = models.ForeignKey(User, related_name="permissions")
collective = models.ForeignKey(Collective, null=True)
course = models.ForeignKey(Course, null=True)
redistribute = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
accessed = models.DateTimeField(auto_now=True)
course/models.py
from django.db import models
from django.utils.translation import ugettext_lazy as _
from competency.models import CompetencyProduct
from ecom.models import Product
from rating.models import Rated
class Chapter(models.Model):
seq = models.PositiveIntegerField(name="Sequence", help_text="Chapter number")
name = models.CharField(max_length=128)
note = models.CharField(max_length=128)
class Course(Product, Rated):
level = models.PositiveIntegerField(choices=CompetencyProduct.LEVELS)
chapters = models.ManyToManyField(Chapter)
class Bundle(models.Model):
class Meta:
unique_together = (("product", "chapter"),)
product = models.ForeignKey(Product, related_name="bundles")
chapter = models.ForeignKey(Chapter, related_name="bundles")
amount = models.PositiveIntegerField()
seq = models.PositiveIntegerField(name="Sequence", default=1)
From what I can see, there's no explicit circular recursion here, save for the required references in __init__.py which appears to be where things are blowing up in my code. Here's the traceback:
File "/path/to/project/virtualenv/lib/python2.6/site-packages/celery/execute/trace.py", line 47, in trace
return cls(states.SUCCESS, retval=fun(*args, **kwargs))
File "/path/to/project/virtualenv/lib/python2.6/site-packages/celery/app/task/__init__.py", line 247, in __call__
return self.run(*args, **kwargs)
File "/path/to/project/virtualenv/lib/python2.6/site-packages/celery/app/__init__.py", line 175, in run
return fun(*args, **kwargs)
File "/path/to/project/django/myproj/elearning/tasks.py", line 5, in decompress
from elearning.models import Elearning
File "/path/to/project/django/myproj/elearning/models.py", line 2, in <module>
from competency.models import CompetencyProduct
File "/path/to/project/django/myproj/competency/models.py", line 5, in <module>
from ecom.models import Product
File "/path/to/project/django/myproj/ecom/models/__init__.py", line 5, in <module>
from ecom.models.permissions import Permission
File "/path/to/project/django/myproj/ecom/models/permissions.py", line 8, in <module>
from course.models import Course
File "/path/to/project/django/myproj/course/models.py", line 4, in <module>
from competency.models import CompetencyProduct
ImportError: cannot import name CompetencyProduct
All I'm trying to do here is import that Elearning model, which is a subclass of CompetencyProduct, and in turn, Product. However, because Product comes from a break-up of the larger ecom/models.py, the ecom/__init__.py file contains the obligatory import of all of the broken-out models, including Permission which has to import Course which requires CompetencyProduct.
The wacky thing is that the whole site works pefectly. Logins, purchases, everything. This problem only occurs when I'm trying to run celery in the background and a new task is loaded or I try to run a shell script using the Django environment.
Is my only option here to remove Permission from the ecom app, or there a better, smarter way to handle this? Additionally, any comments on how I've laid out the project in general are appreciated.
Your problem is that Permission imports Product, but both are imported in ecom/models/__init__.py. You should find a way to either have these two models in the same file or separate them into two apps.

Categories