models.py
TITLE = (
('Classroom', 'Classroom'),
('Playground', 'Playground'),
('Staff Room','Staff Room'),
)
class Location(models.Model):
user = models.ForeignKey(User,null=True)
title = models.CharField('Incident Type', max_length=200,default=TITLE)
parent_location_id = models.CharField('Parent Location', max_length=100, null=True, blank=True)
is_active = models.BooleanField('Is Active', default=True)
def location_title(sender, instance, created, **kwargs):
if instance.is_superuser and not instance.location.is_active:
instance.location.is_active=True
instance.location.save()
post_save.connect(location_title, sender=User)
I want to insert the default data into database with certain conditions.This should happen while creating superuser via manage.py createsuperuser comment.
I don't know it is possible with django,but it is the requirement.I tried with the above code.I am getting the error "AttributeError: 'User' object has no attribute 'location'
" while creating superuser.
The sample what i required is given below
Try this function as signal handler:
def location_title(sender, instance, created, **kwargs):
# Don't fire up on updates.
if not created:
return
# Only handle new superusers.
if not instance.is_superuser or not instance.is_active:
return
# Create a `Location` entry for new superuser.
l = Location(user_id=instance.pk)
l.save()
post_save.connect(location_title, sender=User)
Adding choices to model field:
Django CharField has a named argument choices that allows you to give the end user a list of possible values, and a proper validation of them in forms. The format of the iterable is as follows <internal_value>, <display_value>. Once a field has been passed a choices argument, you can access display value connected to it's internal value with instance.get_<field_name>_display() method.
The choices iterable could look like this:
class Location(models.Model):
class Title:
CLASSROOM = 'classroom'
PLAYGROUND = 'playground'
STAFF_ROOM = 'staff_room'
TITLE_CHOICES = (
(Title.CLASSROOM, 'Classroom'),
(Title.PLAYGROUND, 'Playground'),
(Title.STAFF_ROOM, 'Staff Room'),
)
user = models.ForeignKey(User,null=True)
title = models.CharField('Incident Type', max_length=200,choices=TITLE_CHOICES,default=Title.CLASSROOM)
parent_location_id = models.CharField('Parent Location', max_length=100, null=True, blank=True)
is_active = models.BooleanField('Is Active', default=True)
The final solution is the following:
class Location(models.Model):
class Title:
CLASSROOM = 'classroom'
PLAYGROUND = 'playground'
STAFF_ROOM = 'staff_room'
BASE_LOCATION = Title.CLASSROOM
TITLE_CHOICES = (
(Title.CLASSROOM, 'Classroom'),
(Title.PLAYGROUND, 'Playground'),
(Title.STAFF_ROOM, 'Staff Room'),
)
user = models.ForeignKey(User,null=True)
title = models.CharField('Incident Type', max_length=200,choices=TITLE_CHOICES,default=Title.CLASSROOM)
parent_location_id = models.CharField('Parent Location', max_length=100, null=True, blank=True)
is_active = models.BooleanField('Is Active', default=True)
def location_title(sender, instance, created, **kwargs):
# Don't fire up on updates.
if not created:
return
# Only handle new superusers.
if not instance.is_superuser or not instance.is_active:
return
# Create a `Location` entry for new superuser.
base = Location(user_id=instance.pk, title=Location.BASE_LOCATION)
base.save()
for value, _ in Location.TITLE_CHOICES:
if value == Location.BASE_LOCATION:
continue
l = Location(user_id=instance.pk, title=value, parent_location_id=base.pk)
l.save()
post_save.connect(location_title, sender=User)
When you create the user or superuser the model instance is created but it does not have corresponding location row. Hence, accessing instance.location.is_active gives you the error.
You can update the signal handler to create the location instance first and then set appropriate attributes. As below:
def location_title(sender, instance, created, **kwargs):
#also check for created flag
if created and instance.is_superuser:
location = Location(user=instance)
location.is_active=True
location.save()
post_save.connect(location_title, sender=User)
If you want to choices for title field you can define that in field. Your definition of the title field is not correct, change it to
title = models.CharField('Incident Type', max_length=200, choices=TITLE,
default='Classroom')
You are incorrectly using detfault=TITLE instead of choices=TITLE.
Related
I am trying to update the user profile in django rest framework
So i am able to create the user profile simultaneously with user creation using signals
Now i am trying to update the profile created:-
//models.py
class User(AbstractBaseUser,PermissionsMixin):
phone_regex=RegexValidator(regex = r'^[6-9]\d{9}$',message='please enter the correct phonenumber')
#name_regex=RegexValidator(regex=r'/^[A-Za-z]+$/',message='Please enter the correct name')
phone=models.CharField(validators=[phone_regex],max_length=15,unique=True)
date_joined=models.DateTimeField(verbose_name='date joined',auto_now_add=True)
last_login=models.DateTimeField(verbose_name='last login',auto_now=True)
active = models.BooleanField(default=True)
staff = models.BooleanField(default=False) # a admin user; non super-user
admin = models.BooleanField(default=False)
first_login=models.BooleanField(default=True)
USERNAME_FIELD='phone'
REQUIRED_FIELDS = []
class UserProfile(models.Model):
# There is an inherent relationship between the Profile and
# User models. By creating a one-to-one relationship between the two, we
# are formalizing this relationship. Every user will have one -- and only
# one -- related Profile model.
GENDER = (
('M', 'Homme'),
('F', 'Femme'),
)
user = models.OneToOneField(
User,related_name="profile", on_delete=models.CASCADE
)
# Each user profile will have a field where they can tell other users
# something about themselves. This field will be empty when the user
# creates their account, so we specify blank=True.
email=models.EmailField(unique=True,validators=[validate_email],max_length=254,blank=True,null=True)
name=models.CharField(max_length=15,blank=True)
dateofbirth=models.DateField(auto_now=False, null=True, blank=True)
Gender=models.CharField(max_length=1, choices=GENDER,blank=True,null=True)
address1 = models.CharField(
"Address",
max_length=1024,
blank=True,
null=True
)
address2 = models.CharField(
"Society",
max_length=1024,
blank=True,
null=True
)
address3 = models.CharField(
"Landmark",
max_length=1024,
blank=True,
null=True
)
zip_code = models.CharField(
"ZIP / Postal code",
max_length=12,
blank=True,
null=True
)
city = models.CharField(
"City",
max_length=1024,
default ="Gurugram",
blank=True,
null=True
)
country = models.CharField(
"Country",
max_length=10,
default="India",
blank=True,
null=True
)
# In addition to the `bio` field, each user may have a profile image or
# avatar. This field is not required and it may be blank.
# A timestamp representing when this object was created.
created_at = models.DateTimeField(auto_now_add=True,blank=True)
# A timestamp representing when this object was last updated.
updated_at = models.DateTimeField(auto_now=True,blank=True)
def __str__(self):
return self.user.phone
#receiver(post_save, sender=User)
def create_profile_for_user(sender, instance=None, created=False, **kargs):
if created:
UserProfile.objects.get_or_create(user=instance)
// Serializer.py
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields=("name",)
class UserSerializer(serializers.ModelSerializer):
user = UserProfileSerializer(many=True)
class Meta:
model = User
fields = ('user', 'phone',)
def create(self, validated_data):
user_data = validated_data.pop('user')
user = User.objects.create(**user_data)
profile = user_data.objects.create(user=user, **validated_data)
return profile
def update(self, instance, validated_data):
print("update function")
# profile_data = validated_data.pop('profile')
# print(profile_data)
profile_data = validated_data.pop('user')
profile = instance.profile
# print("profile is ",profile)
print(profile_data.name)
print("name in validation ",validated_data['name'], " type is ",print(type(validated_data)))
instance.username = validated_data.get('name', instance.username)
instance.email = validated_data.get('email', instance.email)
instance.save()
print("name is ",instance.profile.name )
#profile.Gender = validated_data['Gender'],
#profile.email = validated_data['email'],
#profile.dateofbirth=validated_data['dateofbirth'],
#profile.address1=validated_data['address1'],
##profile.address2=validated_data['address2'],
#profile.address3=validated_data['address3']
# print("1",profile)
#print(profile.Gender)
#print(profile.email)
#print(profile.dateofbirth)
profile.save()
print("2",profile)
print(profile.name)
print("save done")
return instance
//views.py
class ProfileCreateAPIView(CreateAPIView):
def put(self, request, *args, **kwargs):
print("my print 0",request)
header_token = request.META.get('HTTP_AUTHORIZATION', None)
print(header_token)
access_token=header_token.split(' ')[1]
status,user =validate_token(access_token)
print(request.data)
user_profile_serializer = UserProfileSerializer(
instance=user,
data=request.data
)
user_serializer = UserSerializer(
instance=user,
data=request.data)
print("my print 1",user_profile_serializer.is_valid())
print("my print 2",user_serializer.is_valid())
if user_profile_serializer.is_valid() and user_serializer.is_valid():
user_profile_serializer.save()
print("user_profile save done")
user_serializer.save()
print("user save done")
return Response(
status=rt_status.HTTP_200_OK)
# Combine errors from both serializers.
# errors = dict()
# errors.update(user_profile_serializer.errors)
#errors.update(user_serializer.errors)
else:
return Response(status=rt_status.HTTP_400_BAD_REQUEST)
'''
return Response(status=rt_status.HTTP_400_BAD_REQUEST)
'''
Now the issue is when i am hitting the api I am getting user_serializer.is_valid() as TRUE but False from UserProfile.
I feel there is some issue with my getting the profile data.
But I am not able to understand how to resolve the issue .
Any help is much appreciated.
i am trying to create a notifications system but am facing issues doing so.
here is my code:
I created a Notifications model where each entry is a notification. Every time a new entry is made into my SalesTask model , it invokes my signal handler which will create a new entry into my Notifications Model. I will then pass this model into my context and render it in my HTML.
class Notifications(models.Model):
notifications_id = models.AutoField(primary_key=True)
user = models.ForeignKey(User)
time = models.DateTimeField(auto_now=True)
message = models.TextField(max_length=100 ,default ='test')
object_url = models.CharField(max_length=500, default ='test')
is_read = models.BooleanField(default=False)
def CreateTaskNotification(sender,**kwargs):
if kwargs['created']:
notification = Notifications.objects.create(user = kwargs['instance'].salesExtra.username,
message = 'You have been assigned a new task',
object_url = kwargs['instance'].get_absolute_url(self)
)
post_save.connect(CreateTaskNotification,sender=SalesTask)
The issue with this is that my SalesTask Models :
class SalesTask(models.Model):
sales_status= (
('p1','Phase 1'),
('p2','Phase 2'),
('p3','Phase 3'),
('p4','Phase 4'),
)
sales_priority= (
('Urgent','Urgent'),
('Medium','Medium'),
('Low','Low'),
)
task_id = models.AutoField(primary_key=True)
salesExtra= models.ManyToManyField('SalesExtra')
sales_project= models.ForeignKey('SalesProject',on_delete=models.CASCADE)
title = models.TextField(max_length=50 , default='Your Title' )
description = models.TextField(max_length=200 , default='Your Description' )
priority = models.TextField(max_length=10 , choices= sales_priority ,default='Low' )
date_time = models.DateTimeField(auto_now=True)
status = models.TextField(max_length=10, choices= sales_status ,default='p1')
due_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return str(self.task_id)
def get_absolute_url(self):
return reverse('sales-task')
has a many to many field with an extended model of User model (SalesExtra) . This is most probably why i am getting the error message :
'ManyRelatedManager' object has no attribute 'user'
This is my views for creating a new instance of a task:
class SalesTaskDetailView(CreateView):
model = SalesTaskingComments
template_name = 'rnd/task_details.html'
fields = ['comment']
exclude = ['comment_id']
def get_context_data(self, **kwargs):
pk = self.kwargs['pk']
context = super(SalesTaskDetailView, self).get_context_data(**kwargs)
context['sales_task'] = SalesTask.objects.filter(task_id = pk)
context['comments'] = SalesTaskingComments.objects.filter(salesTask__task_id__contains= pk)
return context
def form_valid(self, form):
task = get_object_or_404(SalesTask, task_id=self.kwargs['pk'])
user = get_object_or_404(SalesExtra, user=self.request.user)
form.instance.salesTask = task
form.instance.salesExtras = user
return super(SalesTaskDetailView, self).form_valid(form)
My question therefore is :
1. Is there a way to create a new entry for every SalesExtra object ?
2. Is this the best method for creating a Notifications system? (i am using django 3.0 therefore many applications like django-notifications or django-activities-stream is not compatible.)
If you create a through table for the many to many relationship between SalesTask and SalesExtra, you can add the signal handler to this through table so that whenever a relationship is made between the 2 (a user has presumably been added) a notification will be made. If you save a SalesTask model form that has a multi select for the extras this will be triggered for each selection
class SalesTask(models.Model):
salesExtra = models.ManyToManyField('SalesExtra', through='SalesTaskExtra')
class SalesExtra(models.Model):
pass
class SalesTaskExtra(models.Model):
task = models.ForeignKey(SalesTask, on_delete=models.CASCADE)
extra = models.ForeignKey(SalesExtra, on_delete=models.CASCADE)
def CreateTaskNotification(sender, **kwargs):
if kwargs['created']:
Notifications.objects.create(
user=kwargs['instance'].extra.username,
message='You have been assigned a new task',
object_url=kwargs['instance'].task.get_absolute_url()
)
post_save.connect(CreateTaskNotification, sender=SalesTaskExtra)
I have problem with saving instance Live_In Nested Serializers in Django Rest Framework. Hope your guys help me! I think just a basic issue.
My Serializers:
I think it comes error when I write saving instance
class CityLiveInSerializer(ModelSerializer):
country = CharField(required=False, source='country.name')
class Meta:
model = City
fields = [
'name',
'slug',
'country'
]
class UserEditSerializer(ModelSerializer):
live_in = CityLiveInSerializer(source='profile.live_in')
about = serializers.CharField(source='profile.about')
class Meta:
model = User
fields = [
'username',
'live_in',
'about',
]
def update(self, instance, validated_data):
instance.username = validated_data.get('username', instance.username)
instance.save()
# Update Serializers Profile
if (validated_data.get('profile') is not None):
profile_data = validated_data.pop('profile')
profile = instance.profile
profile.about = profile_data.get('about', profile.about)
profile.save()
if (validated_data.get('live_in') is not None):
live_in_data = validated_data.pop('live_in')
try:
city = City.objects.get(name=live_in_data['name'])
except City.DoesNotExist:
city = City.objects.create(**live_in_data)
instance.profile.live_in = city
instance.profile.save()
return instance
My City Model (Live_in)
class City(BaseCity):
class Meta(BaseCity.Meta):
swappable = swapper.swappable_setting('cities', 'City')
class BaseCity(Place, SlugModel):
name = models.CharField(max_length=200, db_index=True, verbose_name="standard name")
country = models.ForeignKey(swapper.get_model_name('cities', 'Country'), related_name='cities', null=True, blank=True)
My Profile Model
class Profile(models.Model):
# Extend User
user = models.OneToOneField(User, unique=True)
about = models.TextField(max_length=1000, default='', blank=True, null=True)
live_in = models.ForeignKey(City, null=True, blank=True, related_name="live_in")
Data sent by Postman (Json)
{ "live_in": { "name": "Encamp" } }
TraceError:
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/rest_framework/serializers.py" in data
263. self._data = self.to_representation(self.instance)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/rest_framework/serializers.py" in to_representation
488. attribute = field.get_attribute(instance)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/rest_framework/fields.py" in get_attribute
463. raise type(exc)(msg)
Exception Type: AttributeError at /api/v1/users/account/edit/
Exception Value: Got AttributeError when attempting to get a value for field live_in on serializer UserEditSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the User instance.
Original exception text was: 'User' object has no attribute 'live_in'.
First of all you dont need many=True in this case. It required for related objects list, but you will pass only one city.
Secondly live_in is attribute of profile model so you need to update profile and add source argument:
live_in = CityLiveInSerializer(source="profile.live_in")
def update(self, instance, validated_data):
instance.username = validated_data.get('username', instance.username)
instance.save()
# Update Serializers Profile
if (validated_data.get('profile') is not None):
profile_data = validated_data.pop('profile')
profile = instance.profile
profile.about = profile_data.get('about', profile.about)
if (profile_data.get('live_in') is not None):
live_in_data = profile_data.pop('live_in')
try:
city = City.objects.get(name=live_in_data["name"])
except City.DoesNotExist:
city = City.objects.create(**live_in_data)
profile.live_in = city
profile.save()
return instance
In this case you need to allow create city without country, so you need to add null=True, blank=True to country attribute:
class BaseCity(Place, SlugModel):
name = models.CharField(max_length=200, db_index=True, verbose_name="standard name")
country = models.ForeignKey(swapper.get_model_name('cities', 'Country'),
related_name='cities', null=True, blank=True)
I have 2 models that are OneToOne related and model that is FK to 2nd model
models.py
class Legal(TimeStampedModel):
name = models.CharField('Name', max_length=255, blank=True)
class LegalCard(TimeStampedModel):
legal = models.OneToOneField('Legal', related_name='legal_card', on_delete=models.CASCADE)
branch = models.ForeignKey('Branch', related_name='branch', null=True)
post_address = models.CharField('Post address', max_length=255, blank=True)
class Branch(TimeStampedModel):
name = models.CharField('Name',max_length=511)
code = models.CharField('Code', max_length=6)
Using DRF I made them to behave as single model so I can create or update both:
serializer.py
class LegalSerializer(serializers.ModelSerializer):
branch = serializers.IntegerField(source='legal_card.branch', allow_null=True, required=False)
post_address = serializers.CharField(source='legal_card.post_address', allow_blank=True, required=False)
class Meta:
model = Legal
fields = ('id',
'name',
'branch',
'post_address',
)
depth = 2
def create(self, validated_data):
legal_card_data = validated_data.pop('legal_card', None)
legal = super(LegalSerializer, self).create(validated_data)
self.update_or_create_legal_card(legal, legal_card_data)
return legal
def update(self, instance, validated_data):
legal_card_data = validated_data.pop('legal_card', None)
self.update_or_create_legal_card(instance, legal_card_data)
return super(LegalSerializer, self).update(instance, validated_data)
def update_or_create_legal_card(self, legal, legal_card_data):
LegalCard.objects.update_or_create(legal=legal, defaults=legal_card_data)
views.py
class LegalDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset = Legal.objects.all()
serializer_class = LegalSerializer
I'm trying to save this by sending FK as integer (I just want to post id of the branch), but I receive error
ValueError: Cannot assign "2": "LegalCard.branch" must be a "Branch" instance.
Is there any way to pass over only ID of the branch?
Thank you
In Django, if you only need the FK value, you can use the FK value that is already on the object you've got rather than getting the related object.
Assume you have a Legal and Branch object with id's as 1. Then you can save a LegalCard object by:
LegalCard(legal_id=1,branch_id=1,post_address="Istanbul Street No:1")
Just use legal_card.branch_id instead of legal_card.branch to get just an id, not a related object.
And depth = 1
Here are my models
class Note():
note = models.TextField(null=False, blank=False, editable=True)
user = models.ForeignKey(to=User, null=True, blank=True)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey("content_type", "object_id")
And an inline I created this model to incorporate in any admin is below
class NoteInline(GenericTabularInline):
model = Note
extra = 0
What I need here is, I want to see all the current notes but don't want the logged in user to edit them. At the moment user can edit old and add new. So here is what I did,
class NoteInline(GenericTabularInline):
model = Note
extra = 0
def get_readonly_fields(self, request, obj=None):
if obj and 'change' in request.resolver_match.url_name:
return ['note', 'user', ]
else:
return []
But now if user adds new note he sees a disabled (not editable) note text ares. However user can see old fields not editable.
How to implement this functionality?
I am having the same inquiry.
However, I do not care if the fields in the inline are "read only" or not. I just do not want them changed once they are created.
For this purpose, I created a NoteForm in forms.py which raises a validation error if the instance has changed while it has initial data:
class NoteForm(forms.ModelForm):
def clean(self):
if self.has_changed() and self.initial:
raise ValidationError(
'You cannot change this inline',
code='Forbidden'
)
return super().clean()
class Meta(object):
model = Note
fields='__all__'
admin.py:
class NoteInline(GenericTabularInline):
model = Note
extra = 0
form = NoteForm