I have two classes in models.py
class Account(models.Model):
...
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
accounts = models.ManyToManyField(Account, blank=True)
Now in my views.py I have:
def create_account(request):
account = Account(symbol=request.POST['symbol'])
account.save()
However, when the form that calls the create_function is successfully submitted, my Django debug mode throws the error: 'ManyToManyDescriptor' object has no attribute 'create', and SELECT * FROM the relevant table in my database confirms that no row has been added.
After searching this error online, I can confirm that this probably has to do with the fact that the Profile class is many-to-many associated with the Account class and that I should be creating Account objects differently, however I am not sure how to do it correctly.
Related
I have a Client object that has a User and a Company objects as FKs
class Client(models.Model):
user = models.OneToOneField(User)
company = models.ForeignKey(Company, blank=True, null=True)
In my CompanyAdmin, I want a list of all clients as a TabularInline, including the active status of each client (based on user object)
class CompanyClients(admin.TabularInline):
model = Client
fields = ('user', 'master')
class CompanyAdmin(admin.ModelAdmin):
inlines = [CompanyClients]
I want to add a column in my TabularInline to indicate that that user is active or not. I tried using 'user__is_active' to my fields but I get this error:
Unknown field(s) (user__is_active) specified for Client
I just want an indication even if read-only (which I tried by putting a readonly_fields and it didn't work too), not looking to save/edit each client user's active status value from the Tabular form.
How can I approach this? Thanks in advance.
class CompanyClients(admin.TabularInline):
model = Client
fields = ('user', 'master','user__active)
user__active
I am writing tests for a large Django application with multiple apps. As part of this process I am gradually creating factories for all models of the different apps within the Django project.
However, I've run into some confusing behavior with FactoryBoy
Our app uses Profiles which are linked to the default auth.models.User model with a OneToOneField
class Profile(models.Model):
user = models.OneToOneField(User)
birth_date = models.DateField(
verbose_name=_("Date of Birth"), null=True, blank=True)
( ... )
I created the following factories for both models:
#factory.django.mute_signals(post_save)
class ProfileFactory(factory.django.DjangoModelFactory):
class Meta:
model = profile_models.Profile
user = factory.SubFactory('yuza.factories.UserFactory')
birth_date = factory.Faker('date_of_birth')
street = factory.Faker('street_name')
house_number = factory.Faker('building_number')
city = factory.Faker('city')
country = factory.Faker('country')
avatar_file = factory.django.ImageField(color='blue')
tenant = factory.SubFactory(TenantFactory)
#factory.django.mute_signals(post_save)
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = auth_models.User
username = factory.Faker('user_name')
first_name = factory.Faker('first_name')
last_name = factory.Faker('last_name')
email = factory.Faker('email')
is_staff = False
is_superuser = False
is_active = True
last_login = factory.LazyFunction(timezone.now)
profile = factory.RelatedFactory(ProfileFactory, 'user')
Which I then run the followings tests for:
class TestUser(TestCase):
def test_init(self):
""" Verify that the factory is able to initialize """
user = UserFactory()
self.assertTrue(user)
self.assertTrue(user.profile)
self.assertTrue(user.profile.tenant)
class TestProfile(TestCase):
def test_init(self):
""" Verify that the factory is able to initialize """
profile = ProfileFactory()
self.assertTrue(profile)
All tests in TestUser pass, but the TestProfile fails on the factory initialization ( profile = ProfileFactory()) and raises the following error:
IntegrityError: duplicate key value violates unique constraint "yuza_profile_user_id_key"
DETAIL: Key (user_id)=(1) already exists.
Its not clear to me why a duplicate User would already exist, (there should only be one call to create one right?, especially since any interfering signals have been disabled)
My code was based on the example from the FactoryBoy documentation, which also dealt with Users / Profiles that are connected via a OneToOneKey
Does anyone know what I am doing wrong?
Update
As per the suggestions of both Bruno and ivissani I've changed the user line in the ProfileFactory to
user = factory.SubFactory('yuza.factories.UserFactory', profile=None)
Now all the tests described above pass successfully!
However I still run into the following issue - when other factories call the UserFactory the
IntegrityError: duplicate key value violates unique constraint "yuza_profile_user_id_key"
DETAIL: Key (user_id)=(1) already exists.
still returns.
I've included an example of a factory calling the UserFactory below, buts its happening to every factory that has a user field.
class InvoiceFactory(factory.django.DjangoModelFactory):
class Meta:
model = Invoice
user = factory.SubFactory(UserFactory)
invoice_id = None
title = factory.Faker('catch_phrase')
price_paid = factory.LazyFunction(lambda: Decimal(0))
tax_rate = factory.LazyFunction(lambda: Decimal(1.21))
invoice_datetime = factory.LazyFunction(timezone.now)
Changing the user field on the InvoiceFactory to
user = factory.SubFactory(UserFactory, profile=None)
Helps it pass some of the tests, but eventually runs into trouble since it no longer has a profile associated with it.
Weirdly the following (declaring the user before the factory) DOES work:
self.user = UserFactory()
invoice_factory = InvoiceFactory(user=self.user)
Its not clear to me why I still keep running into the IntegrityError here, calling the UserFactory() now works fine.
I think it's because your ProfileFactory creates a User instance, using the UserFactory which itself tries to create a new Profile instance using the ProfileFactory.
You need to break this cycle, as described in the documentation you link to:
# We pass in profile=None to prevent UserFactory from
# creating another profile (this disables the RelatedFactory)
user = factory.SubFactory('yuza.factories.UserFactory', profile=None)
If this doesn't work for you and you need more advanced handling, then I suggest implementing a post_generation hook where you can do more advanced things.
EDIT:
Another option is to tell Factory Boy to not recreate a Profile if there is already one for the User by using the django_get_or_create option:
#factory.django.mute_signals(post_save)
class ProfileFactory(factory.django.DjangoModelFactory):
class Meta:
model = profile_models.Profile
django_get_or_create = ('user',)
If you do so, you might be able to remove the profile=None that I suggested before.
EDIT 2:
This might also help, change the UserFactory.profile using a post_generation hook:
#factory.django.mute_signals(post_save)
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = auth_models.User
...
# Change profile to a post_generation hook
#factory.post_generation
def profile(self, create, extracted):
if not create:
return
if extracted is None:
ProfileFactory(user=self)
EDIT 3
I've just realised that the username field in your UserFactory is different from the one in factroy boy's documentation, and it's unique in Django. I wonder if this doesn't cause some old instances to be reused because the username is the same.
You may want to try changing this field to a sequence in your factory:
#factory.django.mute_signals(post_save)
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = auth_models.User
# Change to sequence to avoid duplicates
username = factory.Sequence(lambda n: "user_%d" % n)
I really want to build this app with Django that lets people register and create User instances that can be edited. Each User instance is already linked to a UserProfile with OneToOne because I didn't want to mess with the original User model. The UserProfile will have a field where he/she can register a game if that person is logged in.
ie. Billy wants to register for Monday Smash Melee. He logs in, clicks an option on a form, the UserProfile linked to User, Billy, will update the registered game choice and user tag to the user profile.
The part with the user profile linking to the user works fine, but I don't know how to update the UserProfile with the new tournament registration form so that it can change the UserProfile fields that's linked to the user that is logged in.
Django Models:
class UserProfile(models.Model):
#User profile for registered users. SEPARATE USERBASE TO PLAYER_RANKING
#To Do: add more customizeability and more access for registered.
#weekly e-mails, ability to register for weeklies...
user = models.OneToOneField(User)
picture = models.ImageField(upload_to='profile_images', blank=True)
MON = 'ME'
TUE = 'S4'
THR = 'PM'
reg_game_choices = (
(MON, "Melee"),
(TUE, "Smash 4"),
(THR, "PM"),
)
reg_game_choice = models.CharField(max_length=2,
choices=reg_game_choices,
default="")
user_tag = models.CharField(max_length=60, default = "")
def __str__(self):
return self.user.username
Forms:
class UserForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput())
class Meta:
model = User
fields = ('username', 'password')
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('picture',)
class TournyRegForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('reg_game_choice', 'user_tag',)
View:
#login_required
def tourny_reg(request):
#Registering for tournaments
context_dict = {}
weekday = datetime.datetime.today().weekday()
day_names = ['MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY']
game_days = ['SMASH MELEE', 'SMASH 4', 'CLOSED', 'PROJECT M & FIGHTING GAMES',
'FRIENDLIES', 'CLOSED', 'CLOSED']
day_title = day_names[weekday]
game_day = game_days[weekday]
context_dict['day'] = day_title
context_dict['game_of_the_day'] = game_day
if request.method == 'POST':
tourny_form = TournyRegForm(data=request.POST)
if tourny_form.is_valid():
tourny_form.save()
else:
print (tourny_form.errors)
else:
tourny_form = TournyRegForm()
context_dict['tourny_form'] = tourny_form
return render(request, 'Kappa/tourny_reg.html', context_dict)
It shows up perfectly fine in html and on the local server, but when I try, it gives me an integrity error.
IntegrityError at /Kappa/tourny_reg/
NOT NULL constraint failed: Kappa_userprofile.user_id
Exception Value:
NOT NULL constraint failed: Kappa_userprofile.user_id
▶ Local vars
C:\Users\Kyle\Documents\GitHub\Kappa_Ranks\Kappa\views.py in tourny_reg
So basically, you want to know how to save an instance of something which is related to the logged-in user. That's easy.
To explain why you are getting a NOT NULL error: Your TournyRegForm class has not been told to display an input field for 'user', so it isn't. So when you go to save the form, None is being filled in for the user field. The database is complaining because a 'NOT NULL' field has a NULL value, which is a problem.. so this error is legitimate.
But it's ok that this field is not on the form.. because you don't want the user telling us who they are via the form, you want to get the information about who they are by the fact that they are logged in. The Django auth module puts this information in the Request object where you can easily get at it. All you need to do is to fill in the correct user before the model is saved, like this:
if tourny_form.is_valid():
# commit= False tells the modelform to just create the model instance
# but don't save it yet.
user_profile = tourny_form.save(commit=False)
# associate this user_profile with the logged in user.. it is always
# present in the request object if you are using django's auth module.
user_profile.user = request.user
# now save it
user_profile.save()
So that takes care of saving a model that is related to the currently logged in user. But you have other problems. For example, do you want to save a new UserProfile each time? I don't think you do.. So on your GET you need to do something like this:
user_profile = UserProfile.objects.filter(user=request.user).first()
tourny_form = TournyRegForm(instance=user_profile)
This will fetch the UserProfile of the currently logged=in user from the database, then initialize the form with that instance, so when the user comes back they will be able to edit their profile.
Now, if you actually want the user to be able to register for multiple games.. you will need a Game model for storing the game information, with one-to-many relationship with your UserProfile. This works by having a ForeignKey field in the Game model which relates it to UserProfile.. so each user will have only one UserProfile but could have multiple Games.
How to create a duplicate of the existing User model which display objects only if a condition is satisfied?
Here in this pic, pending users should show all the users with is_active = True:
In the documentation, there was a similar statement:
pendinguser = models.ForeignKey(User, limit_choices_to={'is_staff': True})
How and where to add this to make this work?
See this example:
you have your user model 'User'
Now create a new model and inherit User model like this
class ProxyUser(User):
objects = ProxyUserManagaer()
class Meta:
proxy = True
Now create you custom definition of this Proxy model in manager function:
class ProxyUserManager(models.Manager):
def get_queryset(self):
return super(FeatureManager, self).get_queryset().filter(<add your custom definiton her>)
I'm trying to configure django_ldap_auth to populate user profile with their region data from LDAP:
I created a model for the profile:
from django.db import models
from django.contrib.auth.models import User
class profile(models.Model):
user = models.ForeignKey(User, unique=True)
region = models.CharField(max_length=128, null=True, blank=True)
def __unicode__(self):
return '{0} profile'.format(self.user.username)
class Meta:
app_label = 'core'
I configured my settings to map to 'l' LDAP attribute (that's how region is marked in LDAP)
AUTH_PROFILE_MODULE = 'core.profile'
AUTH_LDAP_PROFILE_ATTR_MAP = {"region": "l"}
This may be important, the profile class sits inside app/models/UserProfile.py file and models/init.py has from UserProfile import profile statement.
However, I still get the following message in debug and profile won't populate:
search_s('dc=*****,dc=ru', 2, '(sAMAccountName=******)') returned 1 objects: CN=Болотнов Александр,OU=****,OU=****,DC=***,DC=ru
Populating Django user *****
Django user **** does not have a profile to populate
Is there anything I'm missing? userdata such as first/last name and email populate just fine but region won't.
This means that this particular User object does not have an associated profile object. Django does not automatically create profile objects, nor does django-auth-ldap. Typically, you will install a post_save signal handler on the User model to create profiles.
See https://docs.djangoproject.com/en/1.4/topics/auth/#storing-additional-information-about-users.