Django models Foreign key autopopulate - django

Am having two model classes user and accounts linked by together or connected by another table called useraccounts.
What i want is for the useraccounts table to be automatically also be populated with the ids of the user and account table when i submit data to them(user and accounts).
here is my sample models code.
class Account(models.Model):
# fields
id = models.IntegerField(primary_key=True)
t_stamp = models.DateField(default=datetime.datetime.now())
acctno = models.TextField(null=False, unique =True)
acctname = models.TextField(null=False)
status = models.TextField(null=False, choices=STATUS)
accttype = models.TextField(null=False,choices=ACCT_TYPE)
acctclass = models.TextField(null=False, choices=ACCT_CLASS)
min_balance = models.FloatField(default=0)
cur_balance = models.FloatField(default=0)
ava_balance = models.FloatField(default=0)
#fundingsources = models.ManyToManyField(FundingSource)
class Meta:
managed = False
db_table = 'accounts'
def save(self, *args, **kwargs):
super(Account, self).save(*args, **kwargs)
try:
self.useraccount_set.all()[0]
except:
UserAccount.objects.bulk_create([UserAccount(account_id=self,user_id=user) for user in User.objects.all()])
class User(AbstractBaseUser, PermissionsMixin):
id = models.AutoField(primary_key=True)
username = models.TextField(unique=True, null=True)
fullname = models.TextField(null=False)
country = models.TextField(null=True)
email = models.EmailField( unique=True, db_index=True)
phone = models.TextField()
address = models.TextField()
activation_key = models.CharField(max_length=40)
key_expires = models.DateTimeField(null=True)
date_joined = models.DateTimeField(default=timezone.now)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=True)
#accounts = models.ManyToManyField(Account, through='UserAccount')
class Meta:
db_table = "users"
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects = UserManager()
def get_short_name(self):
return self.fullname
def get_username(self):
return self.email
def is_authenticated(self):
return True
#property
def is_staff(self):
"Is the user a member of staff?"
# Simplest possible answer: All admins are staff
return self.is_admin
signals.post_save.connect(create_auth_client, sender=User)
User._meta.get_field_by_name('email')[0]._unique=True
class UserAccount(models.Model):
user_id = models.ForeignKey(User)
account_id = models.ForeignKey(Account)
class Meta:
managed = False
db_table = 'useraccounts'
class UserAccount(models.Model):
user_id = models.ForeignKey(User)
account_id = models.ForeignKey(Account)
class Meta:
managed = False
db_table = 'useraccounts'

You have to do that in model save method:
class Account(models.Model):
id = models.IntegerField(primary_key=True)
def save(self, *args, **kwargs):
super(Account, self).save(*args, **kwargs)
try:
self.useraccount_set.all()[0]
except:
UserAccount.objects.bulk_create([UserAccount(account_id=self,user_id=user) for user in User.objects.all()])
class User(AbstractBaseUser, PermissionsMixin):
id = models.AutoField(primary_key=True)
def save(self, *args, **kwargs):
super(User, self).save(*args, **kwargs)
try:
self.useraccount_set.all()[0]
except:
UserAccount.objects.bulk_create([UserAccount(user_id=self,account_id=account) for account in Account.objects.all()])

Related

QuerySet object has no attribute "email"

I am trying to get a list of all pathologists in my system. I need to filter the user on 2 basis i-e is_pathologist and Lab_Id=request.data[email]
I have tried switching between filter and get but then I get
Authentication.models.User.MultipleObjectsReturned: get() returned more than one User -- it returned 12!
Error traceback here
This is the code of my view
#api_view(['POST'])
def getAllPathologists(request):
user = get_user_model().objects.get(is_pathologist=True)
# If user exists, get the employee
print("user is: ", user)
pathologist = Employee.objects.get(user=user.email, Lab_Id=request.data['email'])
pathologistSerializer = EmployeeSerializer(pathologist, many=True)
return Response(pathologistSerializer.data)
This is user model
class User(AbstractUser):
# Add additional fields here
id = None
email = models.EmailField(max_length=254, primary_key=True)
name = models.CharField(max_length=100)
password = models.CharField(max_length=100)
contact_number = models.CharField(max_length=100)
is_patient = models.BooleanField(default=False)
is_doctor = models.BooleanField(default=False)
is_homesampler = models.BooleanField(default=False)
is_pathologist = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_lab = models.BooleanField(default=False)
date_joined = models.DateTimeField(auto_now=True,editable=False)
last_login = models.DateTimeField(auto_now=True)
first_name = None
last_name = None
username = None
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name', 'password']
objects = CustomUserManager()
def __str__(self):
return self.email
# Ensure that the password is hashed before saving it to the database
def save(self, *args, **kwargs):
self.password = make_password(self.password)
super(User, self).save(*args, **kwargs)
def has_perm(self, perm, obj=None):
return self.is_superuser
This is Employee model
class Employee(models.Model):
user = models.OneToOneField(
get_user_model(), on_delete=models.CASCADE, primary_key=True)
CNIC = models.CharField(max_length=100, unique=True)
Lab_Id = models.ForeignKey(Lab, on_delete=models.CASCADE)
def __str__(self):
return self.user.name
This is employee serializer
class EmployeeSerializer(serializers.ModelSerializer):
userData = UserSerializer(read_only=True, source='user')
email = serializers.EmailField(write_only=True)
password = serializers.CharField(write_only=True)
name = serializers.CharField(write_only=True)
contact_number = serializers.CharField(write_only=True)
is_homesampler = serializers.BooleanField(write_only=True)
class Meta:
model = Employee
# fields = " __all__"
fields = ["CNIC", "Lab_Id", "userData",
"name", "contact_number", "email", "password", "is_homesampler"]
def create(self, validated_data):
print("validated data = ", validated_data)
email = validated_data.pop("email")
password = validated_data.pop("password")
name = validated_data.pop("name")
contact_number = validated_data.pop("contact_number")
is_homesampler = validated_data.pop("is_homesampler")
user = get_user_model().objects.create_user(
email=email, password=password, name=name, contact_number=contact_number)
if (is_homesampler):
user.is_homesampler = True
else:
user.is_pathologist = True
user.save()
EmployeeObj = Employee.objects.create(user=user, **validated_data)
return EmployeeObj
You are getting objects and querysets conflated, filter() will return a queryset whereas get() tries to return an object. Below are the reasons for your errors:
The reason for your error with filter() is that a queryset is essentially a group of user objects. The queryset itself has no attribute email, but each user object within the group would. You therefore need to extract a single user from the queryset using first() or last(), for example.
Your error with get() is that your parameters are too broad and thus 12 users are returned. You need to adjust your code to handle this, it's usually done with either a try/except block or using the get_object_or_404 Django shortcut. Once you successfully get the user object, you can call user.email without issue.

Django restrict foreign key options

So I have three models:
class Session(models.Model):
id = models.UUIDField('ID', default=uuid.uuid4, primary_key=True, editable=False)
start_time = models.TimeField('Start Time', default=None)
end_time = models.TimeField('End Time', default=None)
def __str__(self):
return "{}-{}".format(str(self.start_time), str(self.end_time))
class Slot(models.Model):
id = models.UUIDField('ID', default=uuid.uuid4, primary_key=True, editable=False)
timings = models.ForeignKey('Session', on_delete=models.DO_NOTHING, related_name='slot_timings')
available_counsellors = models.ManyToManyField(User, limit_choices_to={'role': 'COUNSELLOR'}, related_name='available_counsellors')
def __str__(self):
return str(self.timings)
class ChatSession(models.Model):
def get_access_code():
while True:
access_code = get_random_string(length=6, allowed_chars=('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'))
if not ChatSession.objects.filter(access_code=access_code).exists():
return access_code
id = models.UUIDField('ID', default=uuid.uuid4, primary_key=True, editable=False)
client = models.ForeignKey(User, limit_choices_to={'role': 'CLIENT'}, on_delete=models.DO_NOTHING, related_name='client_user')
counsellor = models.ForeignKey(User, limit_choices_to={'role': 'COUNSELLOR'}, on_delete=models.DO_NOTHING, related_name='counsellor_user')
access_code = models.CharField('Access Code', default=get_access_code, max_length=6)
topic = models.CharField('Topic', default=None, blank=True, null=True, max_length=255)
slot = models.ForeignKey(Slot, on_delete=models.DO_NOTHING, default=None)
def __str__(self):
return str(self.topic)
In the chat session model, I want to limit the options for counsellor field to the available_counsellors list in slot model.
How can I do this??
I want the same to reflect in my admin view also.
You need to update the queryset on the counsellor form field.
Assuming you are using a ModelForm, you can do this in your view:
chat_session = ChatSession.objects.get(pk=1)
form = ChatSessionForm(instance=chat_session)
form.fields["counsellor"].queryset = chat_session.slot.available_counsellors
You can alternatively do it in your ModelForm:
class ChatSessionForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super (ChatSessionForm, self).__init__(*args, **kwargs)
if self.instance and self.instance.slot:
options = self.instance.slot.available_counsellors
else
options = User.objects.none()
self.fields['counsellor'].queryset = options
For Django admin, something like this should work:
class ChatSessionAdmin(admin.ModelAdmin):
def render_change_form(self, request, context, *args, **kwargs):
if kwargs['obj'].slot:
options = kwargs['obj'].slot.available_counsellors
else:
options = User.objects.none()
context['adminform'].form.fields['counsellor'].queryset = options
return super(ChatSessionAdmin, self).render_change_form(request, context, args, kwargs)

How to update a field in the model using ForegnKey lookup?

I need to be able to update a field num_places in the Event model accessing it via event ForeignKey in the Participant model when the last is saved.
Here are my models.py:
class Event(models.Model):
name = models.CharField(max_length=50)
description = models.CharField(max_length=500)
text = models.TextField()
image = models.ImageField(blank=True)
date = models.DateTimeField()
price = models.PositiveIntegerField()
num_places = models.PositiveIntegerField(default=50)
slug = models.SlugField()
class Participant(models.Model):
name = models.CharField(max_length=200)
participant_uuid = models.UUIDField(primary_key=False, verbose_name='UUID')
email = models.EmailField()
phone_regex = RegexValidator(regex=r'^\+7\d{10}$')
phone_number = models.CharField(validators=[phone_regex], max_length=12)
num_places = models.PositiveIntegerField(default=1)
event = models.ForeignKey(Event, on_delete=models.CASCADE)
paid = models.BooleanField(default=False)
def __str__(self):
return self.name
def save(self, *args, **kwargs):
try:
self.full_clean(exclude=None)
self.event.num_places -= self.num_places # the value isn't updated
super().save(*args, **kwargs)
self.valid = True
self.non_field_errors = False
except ValidationError as e:
self.non_field_errors = e.message_dict[NON_FIELD_ERRORS]
self.valid = False
class Meta:
unique_together = ('name', 'email', 'phone_number', 'event')
The code with a comment has a problem: the num_places value in the Event model stays unchanged. How to fix it?
you need to save the modified object of Event.
self.event.save()
def save(self, *args, **kwargs):
try:
self.full_clean(exclude=None)
self.event.num_places -= self.num_places # the value isn't updated
self.event.save()
"""watch out this since you wanna do it at the end after setting valid, and non_fieild_errors"""
super().save(*args, **kwargs)
self.valid = True
self.non_field_errors = False
except ValidationError as e:
self.non_field_errors = e.message_dict[NON_FIELD_ERRORS]
self.valid = False

request.user.attribute returning unwanted value

Why does the following print(owner) return a different value that what's in my model? Is it possible to get the formattedusername defined below? I've simplified my def profile(request) and took out my other arguments till I can figure out the solution to getting formattedusername.
def profile(request):
owner = User.objects.get (formattedusername=request.user.formattedusername)
args = {'user':request.user.formattedusername}
print (owner)
return render(request, 'accounts/profile.html', args)
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
#3-alphas4numeric
[06/Nov/2017 16:18:11] "GET /account/profile/ HTTP/1.1" 200 1416
formattedusername in my model is stored in the database as HCA\3-alphas4numeric, it's also defined by the following, it's also the key field in all my other models and there isn't a way around using an integer for the key since it's a pre-existing database:
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
username = models.CharField(max_length=7, unique=True)
formattedusername = models.CharField(max_length=11, unique=True, primary_key = True)
first_name = models.CharField(max_length=40)
last_name = models.CharField(max_length=140)
date_joined = models.DateTimeField(default=timezone.now)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
facility = models.CharField(max_length=140)
jobdescription = models.CharField(max_length=140)
positiondescription = models.CharField(max_length=140)
coid = models.CharField(max_length=5)
streetaddress = models.CharField(max_length=140)
USERNAME_FIELD = 'username'
class Meta:
app_label = 'accounts'
db_table = "user"
def save(self, *args, **kwargs):
self.formattedusername = '{domain}\{username}'.format(
domain='HCA', username=self.username)
super(User, self).save(*args, **kwargs);
When you do print(owner), you will print out the value of USERNAME_FIELD. That's how AbstractBaseUser.__str__ is implemented.
def get_username(self):
"Return the identifying username for this User"
return getattr(self, self.USERNAME_FIELD)
def __str__(self):
return self.get_username()

Django REST overrride destroy method to make user inactive

I'm trying to first access the users table via the user foreign key present in userinformations models and later override the RetriveUpdateDestroy API view's destroy method to change the status of the user to inactive instead of deleting them. I can't seem to access the is-active field of the in built User database.
views.py
class UserUpdateApiView(RetrieveUpdateDestroyAPIView):
queryset = UserInformation.objects.all()
serializer_class = UserInformationUpdateSerializer
lookup_field = 'pk'
lookup_url_kwarg = 'id'
def destroy(self, request, *args, **kwargs):
try:
user = User.objects.get(pk=self.kwargs["id"])
deleteStatusVal = False
user.is_active = deleteStatusVal
user.save()
return Response(UserSerializer(user).data)
except:
return Response("Nope")
serializers.py
class UserSerializer(ModelSerializer):
password = serializers.CharField(style={'input_type': 'password'}, write_only=True)
email = serializers.EmailField(validators=[required])
class Meta:
model = User
fields = ['username', 'email', 'password', 'is_active']
extra_kwargs = {'password': {'write_only': True},
'is_active': {'read_only': True}}
def validate(self, data):
email = data.get('email', None)
user = User.objects.filter(email=email).distinct()
if user.exists():
raise ValidationError("That email is already registered!")
return data
class UserInformationUpdateSerializer(ModelSerializer):
user = UserSerializer(read_only=True)
class Meta:
model = UserInformation
fields = ['user', 'first_name', 'middle_name', 'last_name', 'phone', 'date_of_birth']
models.py
class UserInformation(BaseModel):
user = models.OneToOneField(User, related_name='user_id')
first_name = models.CharField(max_length=45)
middle_name = models.CharField(max_length=45, null=True)
last_name = models.CharField(max_length=45)
vendor = models.BooleanField(default=False)
phone = models.CharField(max_length=100, validators=[
RegexValidator(regex=r'^\+?8801?\d{9}$', message="Phone number must be entered in the format: '+8801*********'")
], blank=False, unique=True)
date_of_birth = models.DateField()
confirmation_token = models.CharField(max_length=45, null=True)
confirmation_exp = models.DateTimeField(null=True)
pw_reminder_token = models.CharField(max_length=45, null=True)
pw_reminder_exp = models.DateTimeField(null=True)
profile_pic = models.ImageField(blank=True, null=True, upload_to='profile_images/', default='Images/none/no_images.jpg')
cover_photo = models.ImageField(blank=True, null=True, upload_to='cover_images/', default='Images/none/no_images.jpg')
thumbnail_pic = models.ImageField(blank=True, null=True, upload_to='thumbnail_images/', default='Images/none/no_images.jpg')
phone_verified = models.BooleanField(default=False)
email_verified = models.BooleanField(default=False)
reward_points = models.IntegerField(null=False)
ref_code = models.CharField(null=True, max_length=10)
def __str__(self):
return self.user.username
def delete(self, *args, **kwargs):
self.user.delete()
super(UserInformation, self).delete(*args, **kwargs)
If you want to make User as in active while keeping the UserInformation object and Userobject un-deleted in database, you can do something like this:
def destroy(self, request, *args, **kwargs):
user = self.get_object().user
user.is_active = False
user.save()
return Response(UserInformationUpdateSerializer(self.get_object()).data)
You have 'is_active': {'read_only': True}}.
Also,
# this seems redundant
def delete(self, *args, **kwargs):
self.user.delete()
super(UserInformation, self).delete(*args, **kwargs)