I get Integrity with the below defined model.It occurs when I make changes and save the data again from the django admin.
The error is triggered by obj.save() .
The error is as follows:
Exception Type: IntegrityError at /admin/users/data/1/change/
Exception Value: UNIQUE constraint failed: users_data.id
How can I make this right.
class Data(models.Model):
author = models.ForeignKey(User, null=True, editable=False, on_delete=models.CASCADE)
address = models.CharField(max_length=300,blank=True,null=True,help_text=("enter the address"))
contact = models.IntegerField(blank=True,null=True,help_text=("enter the contact"))
username = models.CharField(max_length=100,blank=True,null=True,help_text=("enter the username"))
password = models.CharField(max_length=100,blank=True,null=True,help_text=("enter the strong password"))
creation_date = models.DateTimeField(editable=False,null=True)
last_modified = models.DateTimeField(editable=False,null=True)
def save(self,*args, **kwargs):
if not self.creation_date:
self.creation_date = timezone.now()
self.last_modified = timezone.now()
return super(Data, self).save(self,*args, **kwargs)
def __str__(self):
return (self.username)
#receiver(post_save,sender=Data)
def datasaver(sender, instance, **kwargs):
address = instance.address
contact = instance.contact
username = instance.username
user_password = instance.password
class CustomAdmin(admin.ModelAdmin):
search_fields = ('first_name', 'email', 'username', )
def save_model(self, request, obj, form, change):
obj.author = request.user
obj.password = ''
obj.save()
def get_queryset(self, request):
qs = super(CustomAdmin, self).get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(author=request.user)
Don't do this. Django provides a better way to save the modified date and creation date. Use this instead and remove the overridden save method and post save signal.
Replace
creation_date = models.DateTimeField(editable=False,null=True)
last_modified = models.DateTimeField(editable=False,null=True)
with
creation_date = models.DateTimeField(auto_now_add=True)
last_modified = models.DateTimeField(auto_now=True)
See django datefield optional arguments
Related
My code was originally implemented for Django 1.8 now (after necessary changes) I'm running it with Django 2.2. Seems that the following was planned to show non-super-user only his/her own files:
class Tiedostot3Admin(admin.ModelAdmin):
fields = ['otsikko', 'kuvaus', 'tiedosto']
list_display = ('otsikko','paivitetty')
inlines = [
Liitet3Inline,
]
def queryset(self, request):
print("queryset, request.user", request.user)
qs = super(Tiedostot3Admin, self).queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(owner=request.user)
def save_model(self, request, obj, form, change):
print("save_model, request.user", request.user)
obj.owner = request.user
obj.save()
When saving new files I can see save_model() executed, but I don't know how to get queryset() executed. It seems that it always shows the same list for all users.
models.py:
class Tiedostot3(models.Model):
otsikko = models.CharField(max_length=250)
kuvaus = RichTextField(blank=True)
paivitetty = models.DateTimeField(auto_now_add=True, verbose_name="Päivitetty")
tiedosto = models.FileField(upload_to='poytakirjat', verbose_name="Tiedosto", blank = True)
owner = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE)
class Meta:
ordering = ['-paivitetty']
verbose_name = "tiedosto"
verbose_name_plural="tiedostot"
def __unicode__(self):
return unicode(self.otsikko)
class Liite3(models.Model):
otsikko = models.CharField(max_length=250)
tiedosto = models.FileField(upload_to='poytakirjat')
doku = models.ForeignKey(Tiedostot3, related_name="liitteet", on_delete=models.CASCADE)
class Meta:
verbose_name_plural="Liitteet"
def __unicode__(self):
return unicode(self.otsikko)
the page:
Implement get_queryset method:
class Tiedostot3Admin(admin.ModelAdmin):
fields = ['otsikko', 'kuvaus', 'tiedosto']
list_display = ['otsikko','paivitetty']
inlines = [Liitet3Inline]
def get_queryset(self, request):
qs = super(Tiedostot3Admin, self).get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(owner=request.user)
def save_model(self, request, obj, form, change):
print("save_model, request.user", request.user)
obj.owner = request.user
obj.save()
I am customizing my Django Admin forms to modify a form field queryset.
The below code works on some of my Models but not others.
The only differences between the models is the name as they all have the same fields:
Club = models.ForeignKey('configuration.Club', on_delete=models.SET_NULL, null = True, blank=True)
Title = models.CharField(max_length=30, blank=False)
Description = models.CharField(max_length=120, blank=True)
Code that throughs an exception (Trophy)
Model:
class Trophy(models.Model):
Club = models.ForeignKey('configuration.Club', on_delete=models.SET_NULL, null = True, blank=True)
Title = models.CharField(max_length=120, blank=False, unique=True)
Description = models.CharField(max_length=360, blank=False, unique=True)
class Meta:
ordering = ['Title', 'Club']
unique_together = ['Title', 'Club']
def __str__(self):
return self.Title
Admin:
class TrophyView(admin.ModelAdmin):
list_display = ('id', 'Club', 'Title', 'Description')
#Modify Form
def get_form(self, request, obj=None, **kwargs):
#Get User's Profile >> Club
user = request.user
profile = vMemberDetails.objects.get(username=user.username)
club = profile.Club_pk
clubID = Club.objects.filter(id=club)
#Super
form = super(TrophyView, self).get_form(request, obj, **kwargs)
form.base_fields['Club'].queryset = clubID
#Modify View
def get_queryset(self, request):
qs = super(admin.ModelAdmin, self).get_queryset(request)
user = request.user
profile = vMemberDetails.objects.get(username=user.username)
club = profile.Club_pk
clubID = Club.objects.get(id=club)
profiles = vMemberDetails.objects.filter(Club_pk=clubID.pk)
if request.user.is_superuser:
return qs
else:
return qs.filter(Club=clubID.pk) | qs.filter(Club__isnull=True)
admin.site.register(Trophy,TrophyView)
When I print(form.base_fields):
{'Club': django.forms.models.ModelChoiceField object at 0x000001E3895D6AF0,
'Title': django.forms.fields.CharField object at 0x000001E3895D6A60,
'Description':django.forms.fields.CharField object at 0x000001E3895D6D30}
When I print(form.base_fields['Club'].queryset):
QuerySet [Club: Test_001 Club, Club: Test_002]
Exception 'NoneType' object has no attribute 'base_fields':
AttributeError at /admin/configuration/trophy/add/
'NoneType' object has no attribute 'base_fields'
Request Method: GET
Request URL: http://localhost:8000/admin/configuration/trophy/add/
Django Version: 3.2.6
Exception Type: AttributeError
Exception Value:
'NoneType' object has no attribute 'base_fields'
Python Version: 3.8.11
Code that returns a modified querset against a similar Model (Target)
Model:
class Target(models.Model):
Club = models.ForeignKey('configuration.Club', on_delete=models.SET_NULL, null = True, blank=True)
Title = models.CharField(max_length=30, blank=False)
Description = models.CharField(max_length=120, blank=True)
class Meta:
ordering = ['Title']
unique_together = ['Title', 'Club']
def __str__(self):
return self.Title
Admin:
class TargetView(admin.ModelAdmin):
list_display = ('id', 'Club', 'Title', 'Description')
#Modify Form
def get_form(self, request, obj=None, **kwargs):
user = request.user
profile = vMemberDetails.objects.get(username=user.username)
club = profile.Club_pk
clubID = Club.objects.filter(id=club)
#Super
form = super(TargetView, self).get_form(request, obj, **kwargs)
form.base_fields['Club'].queryset = clubID
return form
#Modify View
def get_queryset(self, request):
#Get User's Profile >> Club
qs = super(admin.ModelAdmin, self).get_queryset(request)
user = request.user
profile = vMemberDetails.objects.get(username=user.username)
club = profile.Club_pk
clubID = Club.objects.get(id=club)
profiles = vMemberDetails.objects.filter(Club_pk=clubID.pk)
if request.user.is_superuser:
return qs
else:
return qs.filter(Club=clubID.pk) | qs.filter(Club__isnull=True)
admin.site.register(Target,TargetView)
I have a form and a CBV CreateView. I need to assign the request.user to the user field (Foreign Key to CustomUser model), which in the form is a hidden field (user). However, I have tried in the form init method by "self.fields['user'] = self.request.user.id" and I get the error: 'NoneType' object has no attribute 'user'. What is the most clean way of achieving this?
I have tried many different ways of doing it with no luck. ):
This is my form:
class PhoneForm(forms.ModelForm):
class Meta:
model = Phone
fields = 'user', 'phone_name', 'country', 'phone_number', 'phone_type', 'primary'
widgets = {'user': forms.HiddenInput()}
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(PhoneForm, self).__init__(*args, **kwargs)
self.fields['user'].widget.attrs['readonly'] = True
self.fields['user'] = self.request.user.id
self.fields['primary'].help_text = _('<strong>Check:</strong> If this is your main contact phone number.')
def clean(self):
cleaned_data = super(PhoneForm, self).clean()
user = cleaned_data.get('user')
phone_name = cleaned_data.get('phone_name')
country = cleaned_data.get('country')
phone_number = cleaned_data.get('phone_number')
phone_type = cleaned_data.get('phone_type')
primary = cleaned_data.get('primary')
print('user: ',user)
print('phone_name: ',phone_name)
print('country: ',country)
print('phone_number: ',phone_number)
print('phoe_type: ',phone_type)
print('primary: ',primary)
if "action_add" in self.request.POST:
try:
phonenum = Phone.objects.filter(user=user, country=country, phone_number=phone_number).first()
if phonenum:
raise forms.ValidationError(_('This Phone is registered already.'))
except Phone.DoesNotExist:
pass
try:
phone = Phone.objects.filter(user=user, primary=True).first()
if phone and primary:
raise forms.ValidationError(_('There is another "Primary" Phone #already.'), code='invalid')
except Phone.DoesNotExist:
pass
if "action_update" in self.request.POST:
if "country" in self.changed_data or "phone_number" in self.changed_data:
raise forms.ValidationError(_('You can´t update the Country nor the Phone number of the existing Phone.'))
try:
phone = Phone.objects.filter(user=user, primary=True).first()
if phone and primary and self.request.session['Phone_object_id'] != phone.id:
raise forms.ValidationError(_('There is another "Primary" Phone #already.'), code='invalid')
except Phone.DoesNotExist:
pass
if not phone_name and not phone_type and not phone_number:
raise forms.ValidationError(_('You have to fill out the form!'))
This is my view:
class PhoneCreateView(LoginRequiredMixin, CreateView):
form_class = PhoneForm
model = Phone
template_name = 'phones/addPhone.html'
login_url = reverse_lazy('account_login')
success_url = reverse_lazy('phonesList')
class Meta:
readonly = ('user', )
def get_form_kwargs(self):
kw = super(PhoneCreateView, self).get_form_kwargs()
kw['request'] = self.request
return kw
def get_initial(self):
phone = Phone.create(self.request.user.id)
form = self.form_class(instance=phone)
form.instance.user = CustomUser.objects.get(id=self.request.user.id)
return {'form': form, }
def get_context_data(self, **kwargs):
context = super(PhoneCreateView, self).get_context_data(**kwargs)
form = self.form_class(instance=self.object)
form.instance.user = CustomUser.objects.get(id=self.request.user.id)
context = {
'form': form,
'user': self.request.user.id,
}
return context
def form_valid(self, form):
form.instance.user = CustomUser.objects.get(id=self.request.user.id)
form.instance.status = 'A'
form.instance.status_dt = timezone.now()
form.instance.at_user = self.request.user.email
form.instance.at_ip = visitor_ip_address(self.request)
form.instance.at_dttm = timezone.now()
form.instance.at_action = 'Create Phone number'
messages.success(self.request, _('Your Phone Number has been registered.'))
return super(PhoneCreateView, self).form_valid(form)
This is my model:
class Phone(models.Model):
PHONETYPES = (
('1', _('Mobile')),
('2', _('Home')),
('3', _('Work')),
('4', _('Other')),
)
status = models.CharField(_("Record Status"), max_length=1, default='A')
status_dt = models.DateTimeField(_("Status Date"), default=timezone.now)
user = models.ForeignKey(CustomUser, verbose_name=_("User"), on_delete=models.CASCADE)
phone_name = models.CharField(_("Phone name"), max_length=20, default="PH"+str(random.randint(1,99999)))
country = models.IntegerField(_("Country Dialing Code"))
phone_number = models.CharField(_("Phone Number"), max_length=11)
phone_type = models.CharField(_("Phone Type"), max_length=1, default="4", choices=PHONETYPES)
verified = models.BooleanField(verbose_name=_('verified'), default=False)
primary = models.BooleanField(verbose_name=_('primary'), default=False)
at_user = models.CharField(_("Audit Trail (user)"), max_length=255, blank=True)
at_ip = models.GenericIPAddressField(_("Audit Trail (IP)"), protocol='both', blank=True, null=True)
at_dttm = models.DateTimeField(_("Audit Trail (Date-Time)"), default=timezone.now)
at_action = models.CharField(_("Audit Trail (Function performed)"), max_length=255, blank=True)
UniqueConstraint(fields=['user', 'country', 'phone_number'], name='user_unique_phone_number')
class Meta:
verbose_name = _("Phone Number")
verbose_name_plural = _("Phone Numbers")
#classmethod
def create(cls, user):
phone = cls(user= get_object_or_404(CustomUser, pk=user))
# phone create
return phone
def __str__(self):
return self.phone_name
I had to change a few things. First the hidden field user (which is foreign key to the user table) was taken out from the form and from the fields. Instead I used a session parameter where I stored the user id. Then I had to reformulate my method get_context_data in the view. The following is the updated code:
My new form:
class PhoneForm(forms.ModelForm):
class Meta:
model = Phone
fields = 'phone_name', 'country', 'phone_number', 'phone_type', 'primary'
#widgets = {'user': forms.HiddenInput()}
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
if self.request is None:
self.request = kwargs['instance']
super(PhoneForm, self).__init__(*args, **kwargs)
#self.fields['user'].widget.attrs['readonly'] = True
self.fields['primary'].help_text = _('<strong>Check:</strong> If this is your main contact phone number.')
def clean(self):
cleaned_data = super(PhoneForm, self).clean()
customUser = self.request.session['user_id']
phone_name = cleaned_data.get('phone_name')
country = cleaned_data.get('country')
phone_number = cleaned_data.get('phone_number')
phone_type = cleaned_data.get('phone_type')
primary = cleaned_data.get('primary')
try:
del self.request.session['validation_error']
except KeyError:
pass
if "action_add" in self.request.POST:
try:
phonenum = Phone.objects.filter(customUser=customUser, country=country, phone_number=phone_number).first()
if phonenum:
#messages.error(self.request, _('This Phone is registered already.'))
raise forms.ValidationError(_('This Phone is registered already.'))
except Phone.DoesNotExist:
pass
try:
phone = Phone.objects.filter(customUser=customUser, primary=True).first()
if phone and primary:
#messages.error(self.request, _('There is another "Primary" Phone already.'))
raise forms.ValidationError(_('There is another "Primary" Phone #already.'), code='invalid')
except Phone.DoesNotExist:
pass
if "action_update" in self.request.POST:
if "country" in self.changed_data or "phone_number" in self.changed_data:
raise forms.ValidationError(_('You can´t update the Country nor the Phone number of the existing Phone.'))
try:
phone = Phone.objects.filter(customUser=customUser, primary=True).first()
if phone and primary and self.request.session['Phone_object_id'] != phone.id:
raise forms.ValidationError(_('There is another "Primary" Phone #already.'), code='invalid')
except Phone.DoesNotExist:
pass
if not phone_name and not phone_type and not phone_number:
raise forms.ValidationError(_('You have to fill out the form!'))
return cleaned_data
and my new get_context_data method in the view is:
def get_context_data(self, **kwargs):
#Use this to add extra context.#
context = super(PhoneCreateView, self).get_context_data(**kwargs)
form = self.form_class(instance=self.object)
form.instance.customUser = CustomUser.objects.get(id=self.request.user.id)
self.request.session['user_id'] = self.request.user.id
return context
I will try to make a check for the uniqueness of the field at the validation level in the serializer and can not understand why the validator is not called at all.
models.py
class Vendor(models.Model):
active = models.BooleanField(default=False)
...
class VendorContacts(models.Model):
vendor = models.ForeignKey('Vendors', related_name='contacts', on_delete=models.CASCADE)
email = models.CharField(max_length=80, blank=True, null=True)
.....
serializer.py
class VendorContactCreateSerializer(serializers.ModelSerializer):
email = serializers.CharField(validators=[RegexValidator(regex=r'[^#]+#[^\.]+\..+',
message='Enter valid email address')])
vendor = serializers.PrimaryKeyRelatedField(queryset=Vendors.objects.all(), required=False, allow_null=True)
class Meta:
model = VendorContacts
fields = (.....
)
def create(self, validated_data):
.....
#some logic
def validate_email(self, value):
print('Start validation')
exist_contact = VendorContacts.objects.filter(email=value)
if exist_contact:
vc = get_object_or_404(VendorContacts, email=value)
v = vc.vendor
if v.active:
raise serializers.ValidationError('Email {} already exists'.format(value))
return value
In the above serializer, I perform a check at the def validate_email() model field level.
print('Start validation') is not called.
I tried the same at the object level through def validate(): but it is not called either.
UPD
views.py
class VendorContactsCreateView(APIView):
permission_classes = [permissions.AllowAny, ]
serializer_class = VendorContactCreateSerializer
def post(self, request, *args, **kwargs):
data = request.data
serializer = VendorContactCreateSerializer(data=data)
try:
serializer.is_valid(raise_exception=True)
serializer.save()
except ValidationError:
return Response({"errors": (serializer.errors,)},
status=status.HTTP_400_BAD_REQUEST)
else:
return Response(request.data, status=status.HTTP_200_OK)
I am trying to update the various fields of a user model when the user wants to update it which was created earlier. I am having a custom user model and I am using django rest framework for the update api.
views.py
class UserUpdate(generics.UpdateAPIView):
"""
Update user.
"""
parser_class = (FileUploadParser,)
permission_classes = (AllowAny,)
queryset = User.objects.all()
serializer_class = UserUpdateSerializer
def update(self, request, *args, **kwargs):
instance = self.get_object()
instance.user_id = request.data.get("user_id")
instance.save()
serializer = self.get_serializer(instance, data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
return Response(serializer.data)
models.py
class User(models.Model):
USER_CHOICES = (
(1, u'ADMIN'),
(2, u'SALES'),
(3, u'KITCHEN'),
(4, u'EMPLOYEE'),
)
image = models.ImageField(upload_to='employees/', null = True, blank = True)
name = models.CharField(max_length=50)
user_id = models.CharField(max_length=30, primary_key = True, blank = False)
email = models.CharField(max_length=50)
password = models.CharField(max_length=100)
is_active = models.BooleanField(default=True)
user_group = models.PositiveSmallIntegerField(default=4, choices=USER_CHOICES)
firebase_token = models.CharField(max_length=150, default=None, null = True)
shop = models.ForeignKey(Shop, on_delete=models.SET_DEFAULT, default=None)
phone = PhoneField(blank=False, default=0)
serializers.py
class UserUpdateSerializer(serializers.ModelSerializer):
shop = serializers.CharField()
class Meta:
model = User
fields = ('image', 'url', 'phone', 'name', 'user_id','email', 'password', 'is_active', 'user_group', 'shop')
def update(self, instance, validated_data):
shop = validated_data.pop('shop')
user_id = validated_data.pop("user_id")
print(user_id)
shopId = Shop.objects.filter(name=shop).values('shop_id').first()
if shopId is not None:
shop_id = shopId['shop_id']
try:
if user_id is not None:
instance.name = validated_data.get('name', instance.name)
instance.image = validated_data.get('image', instance.image)
instance.email = validated_data.get('email', instance.email)
instance.phone = validated_data.get('phone', instance.phone)
instance.password = validated_data.get('password', instance.password)
instance.user_group = validated_data.get('user_group', instance.user_group)
instance.shop_id = validated_data.get('shop_id', instance.shop_id)
instance.is_active = True
instance.save()
return instance
else:
print("Test")
except Exception as e:
return e
else:
return None
This is throwing an error saying that the user already exists!
Please assist!
Just Change your serializer like below
class UserUpdateSerializer(serializers.ModelSerializer):
shop = serializers.CharField()
class Meta:
model = User
fields = ('image', 'url', 'phone', 'name', 'user_id','email', 'password', 'is_active', 'user_group', 'shop')
def update(self, instance, validated_data):
shop = validated_data.pop('shop')
shop_obj = Shop.objects.filter(name=shop).first()
if shop_obj:
instance.shop = shop_obj
return super().update(instance, validated_data)
When you are calling UserUpdate API, it will create every new User for every new user_id.
I don't know why you are updating user_id. Any new user_id will create a new User instance.
If you want to update other than user_id, you need to remove the following line.
Remove these lines from UserUpdate API View.
instance.user_id = request.data.get("user_id")
instance.save()
Remove this line from UserUpdateSerializer
instance.user_id = validated_data.get('user_id', instance.user_id)
Add lookup_field = 'user_id' to UserUpdate API view
Add user_id at url, like
path('userupdate/<str:user_id>/', UserUpdate.as_view())