I have this model in accounts.models:
from django.contrib.auth.models import User
class UserProfile(BaseModel):
user = models.OneToOneField(
User, related_name="user_profile", on_delete=models.CASCADE
)
# ...
def __str__(self) --> str:
return self.user.username
And the following in memberships.models:
class ExternalServiceProfileMembership(BaseModel):
created_at = models.DateTimeField()
expires_at = models.DateTimeField()
profile = models.ForeignKey(
"accounts.UserProfile",
on_delete=models.CASCADE,
related_name="ext_memberships",
)
plan = models.ForeignKey("memberships.MembershipPlan", on_delete=models.CASCADE)
ext_subscription_id = models.CharField(max_length=128)
When I try to access the admin view of an individual ExternalServiceProfileMembership object (for example: http://localhost:8000/admin/memberships/externalserviceprofilemembership/1/change/), the site gets stuck, eventually returning a 503. So I started out commenting out fields in the AdminModel, and once I remove profile, the object change view loaded fine.
I brought back profile into AdminModel but removed UserProfile's __str__() method, and it also worked. Which makes me think the whole issue is with this method; but I have no idea why.
Any help is appreciated!
On the change page for ExternalServiceProfileMembership, the profile dropdown displays the name of every user. This causes one extra query for every user in the dropdown.
The quick fix is to add 'profile' to readonly_fields, autocomplete_fields or raw_id_fields. These three options mean that a single profile is displayed on the change form, so there is only one extra query to fetch the user.
Another approach, which is more complicated, is to create a custom form that overrides the queryset to use select_related to fetch all of the users, then use that form in your model admin.
I have two apps, users and posts, with the models CustomUser and Block in users, and a model Post in posts. I'd like to created a "bookmarked" ManyToMany field for the User, so that they can bookmark any posts they want. It would look something like:
class CustomUser(AbstractUser):
...
neighbors = models.ManyToManyField("CustomUser", blank=True)
blocks = models.ManyToManyField("Block", blank=True)
bookmarked = models.ManyToManyField("Post", blank=True)
...
As you can see, I have quite a few ManyToMany fields already, but they were all for models from the same app users. As for my Post class:
class Post(models.Model):
...
author = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
block = models.ForeignKey(Block, null=True, on_delete=models.SET_NULL)
...
I already imported two models from the users app, CustomUser and Block, into the posts app. I understand that by importing Post into users' models.py, it creates a circular import, at least, it gives me the following error:
ImportError: cannot import name 'CustomUser' from partially initialized module 'users.models' (most likely due to a circular import)
Is there a way to prevent this? I know an option is to just create the bookmarked attribute in the Post model instead of the User model, but I'm reluctant to do so simply because it's a little weird to me personally. Do you know of any other options, or will I have to create the bookmarked attribute in Post as opposed to User?
You can connect the FK relation by using a string as,
author = models.ForeignKey('app_name.CustomUser', on_delete=models.CASCADE)
so that you don't have to import the related model
Implementing custom user for my project,
here is my account/models.py
class myUser(AbstractBaseUser, PermissionsMixin):
#blah
date_joined = models.DateTimeField(auto_now_add=True)
#blah
and my account/admin.py
class myUserDetail(admin.ModelAdmin):
list_display = ('email','password','is_active','is_staff','date_joined',)
fields = list_display
admin.site.register(myUser, myUserDetail)
The list_display works fine, but when I click into a user,
error is raised :
Unknown field(s) (date_joined) specified for myUser. Check fields/fieldsets/exclude attributes of class myUserDetail.
In fact it exists in postgres...
Please help!
The error is being triggered when the ModelForm is created automatically for the admin, specifically if there are missing fields. Because you are using auto_now_add=True, which implicitly sets editable=False, the field cannot be included in the automatically generated form. Because of this, an error is triggered.
I would recommend specifying fields and list_display independently, as they aren't actually the same.
When you use a custom User model in Django you should add the following line to your settings:
AUTH_USER_MODEL = 'accounts.MyUser'
I also think you should subclass from AbstractUser which is an abstract base class implementing a fully featured User model with admin-compliant permissions, and includes email, date_joined, etc. fields.
You can read more about customizing the user model in the Django documentation.
I have just added a new field to one of my models, I have deleted and recreated my database, but when I enter info into the new field, nothing appears to have saved for that field, but the others have.
The field looks like this
class Author(models.Model):
display_name = models.CharField(unique=True,max_length=30)
first_name = models.CharField(max_length=15)
phone = models.CharField(max_length=15, blank=True)
twitter_handle = models.CharField(max_length=20, blank=True)
And I have included it into the fields list in forms.py
class AuthorForm(ModelForm):
class Meta:
model = Author
fields = ['display_name','first_name','twitter_handle','phone']
Any ideas what could be causing this?
Any help appreciated
The code looks fine. Can you provide the rest of your code?
Some databases require that you define a primary_key - depends on your other PKs in the model
Try adding primary_key=True to your display_name field and see if it helps
FIXED
Due to a complex interaction between two forms that are submitted at the same time with various validation constraints, I manually populate the models from the forms in a views function, and I had forgotten to add the twitter_hanle to this, oops sorry, hopefully this will help if someone makes the same oversight.
I've been getting the most weird error ever. I have a Person model
class Person(models.Model):
user = models.OneToOneField(User, primary_key=True)
facebook_id = models.CharField(max_length=225, unique=True, null=True, blank=True)
twitter_id = models.CharField(max_length=225, unique=True, null=True, blank=True)
suggested_person = models.BooleanField(default=False)
I recently added the twitter_id field. When I access the Django admin page, and try to change the 'person' into a suggested_person, I get the following error:
Person with this Twitter id already exists.
I find this error to be extremely strange because the Facebook_id field is designed the exact same way as the Twitter_id field.
What could be the reason for this?
None of the answers clearly describe the root of the problem.
Normally in the db you can make a field null=True, unique=True and it will work... because NULL != NULL. So each blank value is still considered unique.
But unfortunately for CharFields Django will save an empty string "" (because when you submit a form everything comes into Django as strings, and you may have really wanted to save an empty string "" - Django doesn't know if it should convert to None)
This basically means you shouldn't use CharField(unique=True, null=True, blank=True) in Django. As others have noted you probably have to give up the db-level unique constraint and do your own unique checks in the model.
For further reference, see here: https://code.djangoproject.com/ticket/4136
(unfortunately no good solution decided at time of writing)
NOTE:
As pointed out by #alasdair in a comment, that bug has now been fixed - since Django 1.11 you can use CharField(unique=True, null=True, blank=True) without having to manually convert blank values to None
This is an old one but I had a similar issue just now and though I would provide an alternative solution.
I am in a situation where I need to be able to have a CharField with null=True, blank=True and unique=True. If I submit an empty string in the admin panel it will not submit because the blank string is not unique.
To fix this, I override the 'clean' function in the ModelForm, and in there I check if it's a blank string and return the result accordinly.
class MyModelChangeForm(forms.ModelForm):
class Meta:
model = models.MyModel
fields = ['email', 'name', 'something_unique_or_null',]
def clean_something_unique_or_null(self):
if self.cleaned_data['something_unique_or_null'] == "":
return None
else:
return self.cleaned_data['something_unique_or_null']
This fixed the problem for me without having to sacrifice the unique attribute on the model field.
Hope this helps.
EDIT:
You need to change where I have put "something_unique_or_null" to the name of your field. For example "clean_twitter_id".
In Django 1.11, form CharFields will have an empty_value argument, which allows you to use None if the field is empty.
Model forms, including the Django admin, will automatically set empty_value=None if the model's CharField has null=True.
Therefore, you will be able to use null=True, blank=True and unique=True together in your model CharField without the unique constraint causing problems.
Since you have null=True, blank=True and unique=True, django is considering None or blank as a unique entry. Remove the unique constraint and handle the uniqueness part in the code.
It's important to solve this at the model level, not at the form level, since data can enter through APIs, through import scripts, from the shell, etc. The downside of setting null=True on a CharField is that the column could end up with both empty strings and NULLs, which is slightly ambiguous but not generally a problem in my experience. If you're willing to live with that ambiguity, here's how to do it in a few steps:
1) Set null=True, blank=True on the field and migrate in the change.
2) Massage your data so that all existing empty strings are changed to NULLs:
items = Foo.objects.all()
for item in items:
if not item.somefield:
item.somefield = None
item.save()
3) Add a custom save() method to your model:
def save(self, *args, **kwargs):
# Empty strings are not unique, but we can save multiple NULLs
if not self.somefield:
self.somefield = None
super().save(*args, **kwargs) # Python3-style super()
4) Set unique=True on the field and migrate that in as well.
Now you'll be able to store somefield as empty or as a unique value whether you're using the admin or any other data entry method.
If you prefer not to have several migrations, here's an example of how to do it in a single migration:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
def set_nulls(apps, schema_editor):
Event = apps.get_model("events", "Event")
events = Event.objects.all()
for e in events:
if not e.wdid:
e.wdid = None
e.save()
class Migration(migrations.Migration):
dependencies = [
('events', '0008_something'),
]
operations = [
migrations.AlterField(
model_name='event',
name='wdid',
field=models.CharField(blank=True, max_length=32, null=True),
),
migrations.RunPython(set_nulls),
migrations.AlterField(
model_name='event',
name='wdid',
field=models.CharField(blank=True, max_length=32, null=True, unique=True),
),
]
The root of the problem is that Django persist the empty value as empty-string, and not as null. To fix this, you can subclass CharField as follows:
class CharNullField(models.CharField):
description = "CharField that stores NULL"
def get_db_prep_value(self, value, connection=None, prepared=False):
value = super(CharNullField, self).get_db_prep_value(value, connection, prepared)
if value=="":
return None
else:
return value
So get_db_prep_value will make it sure that null gets persisted.
You have to provide default=None in the field
facebook_id = models.CharField(max_length=225,unique=True, null=True,blank=True,default=None)
This worked for me . I used it in phone number. So it can be unique if entered and not mandatory.
you are accepting blank values and expect them to be unique. this means there can only be ONE entry with a blank twitter_id
you can
either remove the unique contraint
remove the blank =True
give a default value for the field ( but default needs to be unique)