Assuming the following scenario at two seperated apps where I want to count the number of post elements of a specific category at my template:
Categories/models.py
from Posts.models import Post
...
class Category(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=20, verbose_name="Title")
...
#property
def posts(self):
return Post.objects.filter(category_id=self.id).count()
Posts/models.py
class Post(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(verbose_name="Title")
content = models.TextField(verbose_name="Content")
tag = models.CharField(verbose_name="Meta", max_length=70, blank=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True)
template.html:
<h5>Number of Post elements: {{ category.posts }}</h5>
sadly this always results in the following error:
ImportError: cannot import name 'Post' from partially initialized
module 'Posts.models' (most likely due to a circular import)
This is likely because you import Posts.models in your Categories.models and vice-versa. You can import this in the property itself:
# no from Posts.models import Post
# …
class Category(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=20, verbose_name="Title")
# …
#property
def posts(self):
from Posts.models import Post
return Post.objects.filter(category_id=self.id).count()
But you do not need to import this model. Django automatically creates a relation in reverse. If you do not specify the related_name, that is modelname_set, so you can acccess this with:
# no from Posts.models import Post
# …
class Category(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=20, verbose_name="Title")
# …
#property
def posts(self):
return self.post_set.count()
Note: Python modules are normally written in snake_case, not PerlCase, so it
should be category, not Category.
You should try using the related name in your property:
class Category(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=20, verbose_name="Title")
...
#property
def posts(self):
return self.post_set.count()
Also, for the record, it seems odd to have Category and Post in separate apps. Likely they should be in a single blog app. Circular imports generally imply you have two apps that are too tightly-coupled. In this case, it should probably be just one app.
No need for that property, you can access the related objects by following the foreign key backwards. See the docs
<h5>Number of Post elements: {{ category.post_set.count }}</h5>
Or you can add a property but directly:
#property
def post_count(self):
return self.post_set.count()
And then:
<h5>Number of Post elements: {{ category.post_count }}</h5>
The hint is in the error as you do indeed have a circular import.
Posts.models.py will have from Categories.models import Category at the top as you are using it as a foreign key and as you are now using Post within the posts method of Category it will have from Posys.models import Post
You need to fix this which you can do in one of three ways depending if you have other models or methods using the Post class from within the Categories model.
If you do then you can use one of these two
Change the foreignKey to pass in a string rather than the Category object so
class Post(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(verbose_name="Title")
content = models.TextField(verbose_name="Content")
tag = models.CharField(verbose_name="Meta", max_length=70, blank=True)
category = models.ForeignKey("Category", on_delete=models.CASCADE, null=True)
Move the import from the top of Categories/models.py inside the posts method so that it imports locally
class Category(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=20, verbose_name="Title")
#property
def posts(self):
from Posts.models import Post
return Post.objects.filter(category_id=self.id).count()
If you don't, and we can't tell from what you have posted, then you can just use the ```post_set`` related name within the Category class and avoid the import at all. https://docs.djangoproject.com/en/3.0/topics/db/models/#be-careful-with-related-name-and-related-query-name
#property
def posts(self):
return self.post_set.count()
This will also fix the issue
Related
After reading all the docs and answers I can find, and burning a whole day, I still can't make this work. Using Django Tables2, I want to show a list of instruments; the instruments table includes a foreign key to an instrumentsType table. When I list the instruments and their attributes, I want to use the foreign key to substitute the textual instrument type description from the other table. I have tried every combination of double underscores and other accessor techniques, but so far all I get is the dreaded -- in the column. (Displaying just the record ID works).
from .models import Instrument
from django_tables2 import A
from instrumenttypes.models import InstrumentType
class InstrumentTable(tables.Table):
id = tables.LinkColumn('instrument_details', args=[A('station_id')])
class Meta:
model = Instrument
template_name = "django_tables2/bootstrap.html"
fields = ("id", "instrument", "nickname", "serialNo",
"instrument__instrumenttype_id__instrumenttypes__id_instrumentType" )
The models involved are:
Instruments model.py
from django.db import models
from instrumenttypes.models import InstrumentType
from stations.models import Station
# Create your models here.
class Instrument(models.Model):
instrument = models.CharField(max_length=40)
instrumenttype = models.ForeignKey(InstrumentType, on_delete=models.CASCADE, null=True)
station = models.ForeignKey(Station, on_delete=models.CASCADE, default=1)
serialNo = models.CharField(max_length=60, null=True, blank=True)
dateAdded = models.DateTimeField("Date Added", null=True, blank=True)
dateRemoved = models.DateTimeField("Date Removed", null=True, blank=True)
status = models.CharField(max_length=10, null=True, blank=True)
nickname = models.CharField(max_length=40, null=True, blank=True)
InstrumentTypes model.py
from django.db import models
class InstrumentType(models.Model):
instrumentType = models.CharField(max_length=40)
Resulting output:
ID Instrument Nickname SerialNo Instrumenttype
4 instr2 nock2 123 —
The most relevant online references I have found are here and here; but having tried the suggestions, no luck. What am I missing?
I've been struggling to get something working too (but I finally did), and I found the examples too brief.
I think you want to get rid of this stuff in the Meta class
"instrument__instrumenttype_id__instrumenttypes__id_instrumentType"
I think Meta.fields should just be a list of field names, and that you refer to the attribute in the other table from the point of view of the type of object you will later pass in to the IntrumentTable constructor (and that is named in the Meta.model attribute:
from django_tables2.utils import Accessor
class InstrumentTable(tables.Table):
instrument_type = tables.Column(accessor=Accessor('instrumenttype.name'))
class Meta:
model = Instrument
template_name = "django_tables2/bootstrap.html"
fields = ("id", "instrument", "nickname", "serialNo", "insrument_type")
Then, in view, make an instance of InstrumentTable
def myview(request):
table_to_render = InstrumentTable(Instrument.objects)
return render(request, sometemplate, {table: table_to_render})
You didn't show your view, and I know there may be a different way. If you have the whole thing in a repo somewhere, leave a link.
I am making review api with django, but I have a problem.
models.py
from django.db import models
import uuid
# Create your models here.
from django.utils.text import slugify
class buildingData(models.Model):
building_name = models.CharField(max_length=50, unique=True)
slug = models.SlugField(unique=True, default=uuid.uuid1)
building_loc = models.CharField(max_length=50)
building_call = models.CharField(max_length=20)
building_time = models.CharField(max_length=50)
def save(self, *args, **kwargs):
self.slug = slugify(self.building_name)
return super().save(*args, **kwargs)
class reviewData(models.Model):
building = models.ForeignKey(buildingData, related_name='reviews', on_delete=models.CASCADE, null=False, blank=False)
review_content = models.TextField()
star_num = models.FloatField()
urls.py
from django.contrib import admin
from django.urls import path
from crawling_data.views import ReviewListAPI
from crawling_data.views import BuildingInfoAPI
urlpatterns = [
path('admin/', admin.site.urls),
path('api/buildingdata/', BuildingInfoAPI.as_view()),
path('api/buildingdata/<slug:slug>/', ReviewListAPI.as_view())
]
I am collecting data with crawling, but...
django.db.utils.IntegrityError: UNIQUE constraint failed: crawling_data_buildingdata.slug
This error occurs.
I've tried deleting migrate file and migration again, but still doesn't work.
Is there any problem on my code or is there other way to solve this?
That is because the uuid1 is generated from your machine ID and time stamp and the machine ID in your case remains constant, that's why they look pretty similar at the end. You may use uuid4() to get a random unique UUID.
Answer from #ZdaR in this answer
You should change the first model to this:
class buildingData(models.Model):
building_name = models.CharField(max_length=50, unique=True)
slug = models.SlugField(unique=True, default=uuid.uuid4)
building_loc = models.CharField(max_length=50)
building_call = models.CharField(max_length=20)
building_time = models.CharField(max_length=50)
Also you can use your slug as primary key with this:
slug = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
Edited:
I see your save function. in your save function you should remove the slug assignment. Or change it to self.slug = None. If you want to have a slug from the building name you must use TextField or CharField in the model:
slug = models.TextField(unique=True, default=uuid.uuid4)
Context
I have the models AppVersion, App & DeployApp. In the AppVersion model users can upload APK files to the filesystem. I am using a pre_save signal to prevent uploading APK files with the same version_code for a specific App like this:
#receiver(pre_save, sender=AppVersion)
def prevent_duplicate_version_code(sender, instance, **kwargs):
qs = AppVersion.objects.filter(app_uuid=instance.app_uuid, version_code=instance.version_code)
if qs.exists():
raise FileExistsError("Version code has to be unique for a specific app")
This signal does what I want, except it also raises the error when I am trying to create an object in the bridge-table DeployApp.
Models
# models.py
class App(models.Model):
app_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, db_index=True)
app_name = models.CharField(max_length=100)
class AppVersion(models.Model):
app_version_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, db_index=True)
app_uuid = models.ForeignKey(App, on_delete=models.CASCADE, related_name='app_versions')
app_version_name = models.CharField(max_length=100)
version_code = models.IntegerField(blank=True, null=True, editable=False)
source = models.FileField(upload_to=get_app_path, storage=AppVersionSystemStorage())
class DeployApp(models.Model):
deploy_app_uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, db_index=True)
app_version = models.ForeignKey(AppVersion, on_delete=models.CASCADE)
device_group = models.ForeignKey(DeviceGroup, on_delete=models.CASCADE)
release_date = UnixDateTimeField()
My guess is that when creating an object of DeployApp the related AppVersion is also saved and thus the pre_save signal is called and raises the Exception.
I also tried to override the save() method for the AppVersion model but the results are the same.
How do I make sure that the Exception only happens upon creating a new AppVersion instance and does not happen when adding or editing a DeployApp instance?
Solved it thanks to Bear Brown his suggestion. I removed the signal and added UniqueConstraint to the AppVersion model like this:
class Meta:
db_table = 'app_version'
constraints = [
models.UniqueConstraint(fields=['app_uuid', 'version_code'], name='unique appversion')
]
I have created a app using the following Model
models.py
class Vendor(models.Model):
name = models.CharField(max_length=100, unique=True, blank=False)
def __str__(self):
return self.name
class Model(models.Model):
name = models.CharField(max_length=100, unique=True, blank=False)
def __str__(self):
return self.name
class Request(models.Model):
job_reference = models.CharField(max_length=100, unique=True, blank=False)
def __str__(self):
return self.job_reference
class Device(models.Model):
Vendor = models.ForeignKey('Vendor')
Model = models.ForeignKey('Model')
device_id = models.CharField(max_length=255, unique=True, blank=True)
is_encrypted = models.BooleanField()
is_medical = models.BooleanField()
request_job_reference = models.ForeignKey('Request')
submitted = models.DateTimeField(default=timezone.now)
When i go to the admin page I can add new devices which displayed each of the fields and the "Vendor" and "Model" allows me to either select an existing entry or has a plus icon to add a new entry (which is great)
Django_Admin_form
When i create a form for my app
forms.py
from django import forms
from . models import Device
class AddDevice(forms.ModelForm):
class Meta:
model = Device
fields = ('Vendor', 'Model', 'device_id', 'is_encrypted', 'is_medical', 'submitted')
The form on my webpage display ok however there is no option to insert a new entry to "Vendor" or "Model".
Webpage Form
I have looked on other posts on here as users have had the same issue and it's been suggested to use "ModelChoiceField" but unfortunately it still doesn't make any sense to me. Either i'm completely missing something or I have setup my models in a way which is making things harder for myself.
Can anyone explain how I can go about doing this?
I have a website built in Django 1.10. The site has 3 different apps: teams, members and news.
The first app, called teams has one model called Team.
This is the Team/models.py:
from django.db import models
from django.db.models.signals import pre_save
from django.utils.text import slugify
class Team(models.Model):
name = models.CharField(max_length=255)
description = models.TextField()
slug = models.CharField(max_length=255, default='team', editable=True)
class Meta:
ordering = ('name',)
def __unicode__(self):
return self.name
The second app, called members has one model called Member.
This is the Member/models.py:
from django.db import models
class Piloto(models.Model):
name = models.CharField(max_length=255)
biography = models.TextField()
slug = models.CharField(max_length=255, default='piloto', editable=True)
class Meta:
ordering = ('name',)
def __unicode__(self):
return self.name
What I want is include the name of the team inside the member profile, so I know it should be something like:
team_of_member = models.ForeignKey();
But I don't know what to put in the parenthesis or how to import the model of the team to the model of the member. I was following the documentation of Django 1.10 but it wasn't working, also I've tried this link but it doesn't work. Could you give a hand? Thanks
Edit:
I tried to do as #Bulva was suggesting, so my code is now like this:
from django.db import models
from equipos.models import Team
class Member(models.Model):
name = models.CharField(max_length=255)
team = models.ForeignKey('teams.Team', null=True)
biography = models.TextField()
slug = models.CharField(max_length=255, default='piloto', editable=True)
class Meta:
ordering = ('name',)
def __unicode__(self):
return self.name
What you want is to provide the teams the member is a member of. It all depends on your business logic. A team has many members by definition, but can a member be a part of many teams? If yes, you have a many-to-many relationship. If no, you have a one-to-many relationship.
Under one-to-many assumption, the foreignkey information has to be put in the referenced model. Then:
from django.db import models
from team.models import Team # Generally, apps are in all lower-case (assuming your app is called team)
class Member(models.Model):
name = models.CharField(max_length=255)
team = models.ForeignKey('team.Team', related_name = 'members', null=True) # Do not forget to put team.Team inside a pair of single-quotes.
biography = models.TextField()
slug = models.CharField(max_length=255, default='piloto', editable=True)
class Meta:
ordering = ('name',)
def __unicode__(self): # use def __str__(self): in Python 3+
return self.name
In your view, you can then say this:
albert_team = albert.team
albert_teammates = albert_team.members
Under Many-to-Many assumption, it is more natural to capture the relationship in the team model:
from django.db import models
from django.db.models.signals import pre_save
from django.utils.text import slugify
class Team(models.Model):
name = models.CharField(max_length=255)
description = models.TextField()
slug = models.CharField(max_length=255, default='team', editable=True)
members = models.ManyToManyField('team.Member', related_name = 'teams')
class Meta:
ordering = ('name',)
def __unicode__(self):
return self.name
In views.py:
albert_teams = albert.teams
all_albert_teammates = []
for team in albert_teams:
all_albert_teammates.append(team.members)
Try this:
from teams.models import Team
# in the member model field
team_of_member = models.ForeignKey(Team);
In order to do what you want (assuming the code provided is your full "problematic" code) you should:
In Member/models.py:
from django.db import models
from teams.models import Team # <--add this line
class Piloto(models.Model):
name = models.CharField(max_length=255)
team_of_member = models.ForeignKey(Team, on_delete=models.CASCADE); # <--add this line
biography = models.TextField()
slug = models.CharField(max_length=255, default='piloto', editable=True)
class Meta:
ordering = ('name',)
def __unicode__(self):
return self.name
After you do this, remember to:
python manage.py makemigrations
python manage.py migrate
to change your models state.
Good luck :)