Django save many to many relationship on custom many to many model - python

Trying to set values for a custom many to many model in Django. My User model has a many to many field named as dispatcher_county. I have created a model to handle many to many relationship between user's who are dispatchers and counties named as DispatcherCounty. Check the create method in UserPostSerializer mentioned below to see how I am creating users who are dispatchers and drivers.
Error Received:
AttributeError: 'ManyToManyField' object has no attribute
'_m2m_reverse_name_cache'
requirements:
Django==3.0.8
djangorestframework==3.11.0
mysqlclient==2.0.1
model.py
class User(AbstractBaseUser):
"""
This is a class for user table which overrides functionalities of default django user model.
Attributes:
name (CharField): Name of a user.
email (EmailField): Email address of a user.
mobile (CharField): Phone number of a user.
date_joined (CharField): When a user was added.
last_login (CharField): Last login date time of a user.
is_admin (CharField): If user is a admin or not.
is_active (CharField): If user is active or not.
is_staff (CharField): If user is staff or not.
is_superuser (CharField): If user is a superuser or not.
role (OneToOneField): One to one relationship with role table.
"""
name = models.CharField(max_length=80)
email = models.EmailField(max_length=255, unique=True)
mobile = models.CharField(
validators=[
RegexValidator(
regex=r"^\d{10,14}$",
message="Phone number must be entered in format: '+999999999'. Up to 14 digits allowed.",
)
],
max_length=15,
unique=True,
)
role = models.ForeignKey(Role, on_delete=models.SET_NULL, null=True)
dispatcher_counties = models.ManyToManyField(
"self",
through="DispatcherCounty",
symmetrical=False,
related_name="dispatcher_has_county",
)
date_joined = models.DateTimeField(auto_now_add=True)
last_login = models.DateTimeField(auto_now=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True)
updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
USERNAME_FIELD = "mobile"
REQUIRED_FIELDS = ["email", "name"]
objects = UserManager()
class Meta:
db_table = "users"
def __str__(self):
return self.email + ", " + self.mobile + ", " + self.name
# For checking permissions. to keep it simple all admin have ALL permissons
def has_perm(self, perm, obj=None):
return self.is_admin
# Does this user have permission to view this app? (ALWAYS YES FOR SIMPLICITY)
def has_module_perms(self, app_label):
return True
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create(user=instance)
class DispatcherDriver(models.Model):
dispatcher = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="dispatcher_driver"
)
driver = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="driver_dispatcher"
)
created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True)
updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
class Meta:
db_table = "dispatcher_driver"
def __str__(self):
return self.dispatcher.name + ", " + self.driver.name
class DispatcherCounty(models.Model):
dispatcher = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="dispatcher_county"
)
county = models.ForeignKey(
County, on_delete=models.CASCADE, related_name="county_dispatcher"
)
created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True)
updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
class Meta:
db_table = "dispatcher_county"
def __str__(self):
return self.dispatcher.name + ", " + self.county.name
serializer.py
class UserPostSerializer(serializers.ModelSerializer):
name = serializers.CharField(required=True, max_length=80)
email = serializers.EmailField(required=True)
mobile = serializers.CharField(required=True, max_length=15,)
role = serializers.CharField(required=True)
dispatcher = serializers.IntegerField(required=False, allow_null=True)
counties = serializers.ListField(
required=False,
child=serializers.IntegerField(),
error_messages={"required": "Counties field is required."},
)
class Meta:
model = User
exclude = ["password"]
def validate(self, data):
# if role is driver
if data["role"].name == "Driver":
# dispatcher is required
if "dispatcher" not in data.keys() or data["dispatcher"] is None:
raise serializers.ValidationError(
"Dispatcher field is required if role is Driver."
)
else:
try:
dispatcher = User.objects.filter_superuser().get(
pk=data["dispatcher"]
)
if dispatcher.role.name != "Dispatcher":
raise serializers.ValidationError("Invalid dispatcher.")
data["dispatcher"] = dispatcher
except User.DoesNotExist:
raise serializers.ValidationError("Dispatcher not found.")
# if role is dispatcher
elif data["role"].name == "Dispatcher":
# counties are required
if (
"counties" not in data.keys()
or data["counties"] is None
or len(data["counties"]) == 0
):
raise serializers.ValidationError(
"Counties field is required if role is Dispatcher. Min 1 county is required."
)
return data
def validate_role(self, value):
role = Role.objects.filter(name=value).exclude(name__iexact="Superuser").first()
if role is None:
raise serializers.ValidationError("Not a valid role.")
return role
def validate_email(self, value):
user = User.objects
if self.instance:
user = user.exclude(pk=self.instance.pk)
if user.filter(email__iexact=value):
raise serializers.ValidationError("User with this email already exists.")
return value
def validate_mobile(self, value):
if validate_phone_number(value) is None:
user = User.objects
if self.instance:
user = user.exclude(pk=self.instance.pk)
if user.filter(mobile__iexact=value):
raise serializers.ValidationError(
"User with this phone number already exists."
)
return value
def create(self, validated_data):
try:
with transaction.atomic():
dispatcher = None
if validated_data["role"].name == "Driver":
dispatcher = validated_data.pop("dispatcher")
counties = None
if validated_data["role"].name == "Dispatcher":
counties = validated_data.pop("counties")
instance = User.objects.create(**validated_data)
if dispatcher is not None:
DispatcherDriver.objects.create(
dispatcher=dispatcher, driver=instance
)
if counties is not None:
counties = County.objects.filter(id__in=counties).all()
if len(counties) == 0:
raise serializers.ValidationError("Invalid counties.")
instance.dispatcher_counties.set(*counties)
return instance
except DatabaseError as database_error:
return None

Related

Update user profile with user uuid

I want to update user profile passing user uuid as kwarg.
Here is the url:
path("profile/update/<uuid:pk>", UpdateProfile.as_view(), name="update_profile"),
However, after I try to update my profile, it gives me an error.
Here is my view:
class UpdateProfile(LoginRequiredMixin, UpdateView):
model = Profile
user_type_fields = {
"Buyer": ["photo", "first_name", "last_name", "city"],
"Celler": ["photo", "name", "city", "address"],
}
def get(self, request, *args, **kwargs):
print(kwargs)
self.fields = self.user_type_fields[get_user_model().objects.get(pk=kwargs["pk"]).type]
return super().get(request, *args, **kwargs)
And here is the error itself:
Page not found (404)
No profile found matching the query
As I understand, django tries to find profile with uuid as in url, doesn't find it and returns me this error. However, if I change model in my view to user, it wouldn't be able to find fields as they belong to profile model. The only working option was to pass profile id as kwarg, but I don`t find it preferrable due to security reasons.
Could someone give me an advice on how to update profile with user uuid in kwargs?
Thanks in advance!
UPD:
Here are User and Profile models:
class CustomUser(AbstractBaseUser, PermissionsMixin):
class UserTypeChoices(models.TextChoices):
SINGLE_VOLUNTEER = "Single Volunteer", _("Single Volunteer")
VOLUNTEERS_ORGANISATION = "Volunteers Organisation", _("Volunteers Organisation")
CIVIL_PERSON = "Civil Person", _("Civil Person")
MILITARY_PERSON = "Military Person", _("Military Person")
type = models.CharField(
max_length=23,
choices=UserTypeChoices.choices,
)
uuid = models.UUIDField(
primary_key=True,
default=uuid4,
unique=True,
db_index=True,
editable=False,
)
email = models.EmailField(
_("email address"),
null=True,
blank=True,
)
phone = PhoneNumberField(
_("phone"),
null=True,
blank=True,
)
is_staff = models.BooleanField(
_("staff status"),
default=False,
help_text=_("Designates whether the user can log into this admin site."),
)
is_active = models.BooleanField(
_("active"),
default=True,
help_text=_(
"Designates whether this user should be treated as active. " "Unselect this instead of deleting accounts."
),
)
def __str__(self):
if self.email:
return str(self.email)
else:
return str(self.phone)
USERNAME_FIELD = "email"
objects = CustomUserManager()
class Profile(models.Model):
user = models.OneToOneField(to="accounts.CustomUser", on_delete=models.CASCADE, blank=True, null=True)
photo = models.ImageField(upload_to="profile/", blank=True, null=True, default="profile/profile_default.png")
name = models.CharField(_("name"), max_length=150, blank=True, null=True, default=None)
first_name = models.CharField(_("first name"), max_length=150, blank=True, null=True, default=None)
last_name = models.CharField(_("last name"), max_length=150, blank=True, null=True, default=None)
city = models.CharField(_("city"), max_length=150, blank=True, null=True, default=None)
address = PlainLocationField()
def __str__(self):
if self.user.email:
return str(self.user.email)
else:
return str(self.user.phone)
Assuming the following model, where a user only has one profile:
class Profile(models.Model):
user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE)
photo = models.ImageField()
# ... your other fields
You can then overwrite the get_object() method:
class UpdateProfile(LoginRequiredMixin, UpdateView):
model = Profile
fields = ['photo', '...']
def get_object(self):
user = get_user_model().objects.get(pk=self.kwargs['pk'])
profile = user.profile
return profile
And then use the UpdateView as normal.

Django | set the default value of a model field to be the email of the logged in user

I'm trying to display user specific content in my django website and I am one step away from completing this task. In models.py I need the contact_email field to have its default value to be the email of the logged in user. I've tried many methods of doing this but nothing has worked yet.
models.py
class Account(AbstractUser):
email = models.EmailField(verbose_name='email', max_length=60, unique=True)
name = models.CharField(max_length=45, unique=False)
username = models.CharField(max_length=100, default='')
date_joined = models.DateTimeField(verbose_name='date joined', auto_now_add=True)
last_login = models.DateTimeField(verbose_name='last login', auto_now=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_employee = models.BooleanField(default=True, verbose_name='Are you using FilmLink as an employee?')
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['name', 'is_employee']
objects = MyAccountManager()
class Meta:
permissions = [
("post_jobs", "Can post jobs"),
]
def __str__(self):
return self.name
def has_perm(self, perm, obj=None):
return True
def has_perms(self, perm):
return True
def has_module_perms(self, app_label):
return True
#property
def is_staff(self):
return self.is_admin
class Job(models.Model):
company = models.CharField(max_length=40, null=True, verbose_name="Company/Employer")
description = models.TextField(null=True)
role = models.CharField(max_length=25)
area_of_filming = models.CharField(max_length=50, verbose_name="Area Of Filming", default="")
contact_email = models.EmailField(verbose_name='Contact Email', max_length=60, default='')#stuck on setting this default
created = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)
def __str__(self):
return self.company
views.py
#login_required(login_url='login')
def manage_jobs(request):
if request.user.is_employee:
return redirect('home')
else:
form = JobForm(request.POST)
if form.is_valid():
form.save()
jobs = Job.objects.filter(contact_email = request.user.email)
context = {"form":form, "jobs":jobs}
return render(request, 'employer/manage-jobs.html', context)
forms.py
class JobForm(ModelForm):
class Meta:
model = Job
fields = ('company', 'description', 'role', 'area_of_filming', 'contact_email')
I don't believe you can set a default that way.
What you should do instead is, when creating a Job object and saving it to database, provide the value of contact_email yourself.
You would then be able to later filter the Job entries based on contact_email field.
# Creating a Job object
job = Job()
job.column1 = value
job.contact_email = request.user.email
# In other view, where you need to filter
jobs = Job.objects.filter(contact_email=request.user.email)
A random thought:
if the only reason you want that contact_email field there is to be able to apply the filter to get all Job objects for that particular user, then you can just filter with user field too.
jobs = Job.objects.filter(user=request.user)
context = {"form":form, "jobs":jobs, 'contact_email': request.user.email}
return render(request, 'employer/manage-jobs.html', context)

Django add to cart as a guest without logging in

I'm working on a ecom website project and I follow Dennis Ivy's course about cart functionality. I want to add to cart whether the person is logged in or not. I place my code down below, where products are added to the cart as a order when you're logged in and everything's fine but when user is not logged in nothing happen even though I created the device key inside cookies.
models:
class User(AbstractBaseUser):
email = models.EmailField(verbose_name='email',max_length=255, unique=True)
first_name = models.CharField(max_length=50, blank=True, null=True)
last_name = models.CharField(max_length=50, blank=True, null=True)
username = models.CharField(max_length=50, default=None, unique=False, blank=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
timestamp = models.DateTimeField(auto_now_add=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name','last_name']
objects = UserManager()
def __str__(self):
return self.email
def has_perm(self, perm, obj=None):
return self.is_admin
def has_module_perms(self, app_label):
return True
class Guest(models.Model):
name = models.CharField(max_length=200, null=True, blank=True)
email = models.CharField(max_length=200, null=True, blank=True)
device = models.CharField(max_length=200, null=True, blank=True)
def __str__(self):
if self.name:
name = self.name
else:
name = self.device
return str(name)
class Order(models.Model):
klient = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
gosc = models.ForeignKey(Guest, on_delete=models.SET_NULL, null=True, blank=True)
data_zamowienia = models.DateTimeField(auto_now_add=True)
dostarczona = models.BooleanField(default=False)
transaction_id = models.CharField(max_length=100, null=True)
produkt = models.ForeignKey(Product, on_delete=models.SET_NULL, null=True)
ilosc = models.IntegerField(default=0, null=True, blank=True)
data_dodania = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.id)
def order_set(self):
pass
views:
def updateItem(request):
data = json.loads(request.body)
productId = data['productId']
action = data['action']
product = Product.objects.get(id=productId)
try:
user = request.user
order, created = Order.objects.get_or_create(klient=user, produkt=product, dostarczona=False)
except:
device = request.COOKIES['device']
customer, created = Guest.objects.get_or_create(device=device)
order, created = Order.objects.get_or_create(gosc=customer, produkt=product, dostarczona=False)
if action == 'add':
order.ilosc = (order.ilosc + 1)
elif action == 'remove':
order.ilosc = (order.ilosc - 1)
order.save()
if order.ilosc <= 0:
order.delete()
return JsonResponse('Item was added', safe=False)
I just did a quick test with the following view.
def myview(request):
print(request.user)
return HttpResponse("hello")
In an unauthenticated session, request.user isn't empty or None by the looks of it.
System check identified no issues (0 silenced).
January 09, 2021 - 21:45:29
Django version 3.1.5, using settings 'config.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
AnonymousUser
[09/Jan/2021 21:45:33] "GET /test/ HTTP/1.1" 200 5
So if you're just doing a truthy check on request.user it's always going to be true.
You could perhaps do the following:
def myview(request):
if request.user != 'AnonymousUser':
# insert logged in user code here
else:
# insert your device related code here

How do i use user_passes_test view specific page?

My User Model
SELLER = "SELLER"
CUSTOMER = "CUSTOMER"
USER_TYPE_CHOICES = (
('SELLER' , 'Seller'),
('CUSTOMER' , 'Customer'),
)
class User(AbstractBaseUser,PermissionsMixin):
email = models.EmailField( max_length=255, unique=True)
user_type = models.CharField(max_length=200,choices=USER_TYPE_CHOICES,null=True)
last_login = models.DateTimeField( blank=True, null=True)
is_staff = models.BooleanField( default=False)
is_superuser = models.BooleanField( default=False)
is_active = models.BooleanField( default=True)
date_joined = models.DateTimeField(default=timezone.now)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
class Meta:
ordering = ['email']
def clean(self):
super().clean()
self.email = self.__class__.objects.normalize_email(self.email)
def get_full_name(self):
return self.email
def get_short_name(self):
return self.email
I have two user_types one seller and one customer how i can use user_passes_test decorator to see if the user.user_type is seller so he can see the seller view only seller can see the seller view
def seller_home(request,pk):
try:
seller = Seller.objects.get(id=pk)
except User.DoesNotExist:
raise Http404("Seller Does Not Exisit")
sellerproducts = Product.objects.filter(seller=seller)
totalsellerproducts = sellerproducts.count()
context = {'seller':seller,'sellerproducts':sellerproducts,"totalsellerproducts":totalsellerproducts }
return render(request,"seller_home.html",context)
I think that you can't, because user_passes_test is for authenticated users only, so the seller or customer cannot be sent by URL to the view, because it will be insecure, anyone will be able to test any pk, You must use the authenticated user in request.user to do this, then the decorator will be easy. Something like this:
def seller_check(user):
return user.user_type == User.SELLER
#user_passes_test(seller_check)
def seller_home(request):
pass

Dynamic Choices (Those that get updated for new values entered.)

In this project, I got three types of users: ChalkSlateAdmin (this is not a superuser, administrator or staff, etc.), Student and Teacher. All of these can be considered ChalkSlateUser as each have a OneToOne Relationship with the model. ChalkSlateAdmin can be considered an institute and any time someone registers as a ChalkSlateAdmin, there is a new institute as ChalkSlateAdmin has that attribute.
Teachers and Students can join institutes (ChalkSlateAdmin attribute) that are already registered. So, I need to create a form that has choices of institutes that are currently registered.
My question is, how can I define choices that change depending on what institutes (ChalkSlateAdmin attribute) are registered? Before this, I used constant string values for choices like male & female.
models.py,
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.db import models
class MyAccountManager(BaseUserManager):
def create_user(self, username, email, password=None):
if not email:
raise ValueError("Users must have an email address.")
if not username:
raise ValueError("Users must have a username.")
user = self.model(
email=self.normalize_email(email),
username=username,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, username, password):
user=self.create_user(
email=self.normalize_email(email),
password=password,
username=username,
)
user.user_type = 1
user.is_admin = True
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
class ChalkSlateUser(AbstractBaseUser):
# required to include
email = models.EmailField(verbose_name='email', unique=True, max_length=60)
username = models.CharField(max_length=60, unique=True)
date_joined = models.DateField(verbose_name='date joined', auto_now_add=True)
last_login = models.DateField(verbose_name='last login', auto_now=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
# new
first_name = models.CharField(max_length=60, verbose_name='first name')
last_name = models.CharField(max_length=60, verbose_name='last name')
has_institute = models.BooleanField(default=False)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username',]
objects = MyAccountManager()
def __str__(self):
return self.email
def has_perm(self, perm, obj=None):
return self.is_admin
def has_module_perms(self, app_label):
return True
AUTHORITY = 1
INSTITUTE = 2
STUDENT = 3
TUTOR = 4
USER_TYPE_CHOICES = (
(AUTHORITY, 'Authority (superuser or admin)'),
(INSTITUTE, 'ChalkSlateAdmin'),
(STUDENT, 'Student'),
(TUTOR, 'Tutor'),
)
user_type = models.PositiveSmallIntegerField(choices=USER_TYPE_CHOICES, null=True, blank=True)
# user_type = models.PositiveSmallIntegerField(null=True, blank=True)
# is_admin = models.BooleanField(default=False)
# is_student = models.BooleanField(default=False)
# is_tutor = models.BooleanField(default=False)
class ChalkSlateAdmin(models.Model):
chalkslate_user = models.OneToOneField(ChalkSlateUser, on_delete=models.CASCADE, null=True)
# username_institute = models.CharField(max_length=100, unique=True)
institute_name = models.CharField(verbose_name='institute name', max_length=100, unique=True)
institute_details = models.CharField(verbose_name='institute details', max_length=100)
def __str__(self):
return self.chalkslate_user.email
class Student(models.Model):
chalkslate_user = models.OneToOneField(ChalkSlateUser, on_delete=models.CASCADE)
# username_student = models.CharField(max_length=100, unique=True)
# name = models.CharField(max_length=100)
student_details = models.CharField(verbose_name='student details', max_length=100)
picture = models.ImageField(verbose_name='picture', upload_to='student_pictures')
def __str__(self):
return self.chalkslate_user.email
class Tutor(models.Model):
chalkslate_user = models.OneToOneField(ChalkSlateUser, on_delete=models.CASCADE)
# username_tutor = models.CharField(max_length=100, unique=True)
# name = models.CharField(max_length=100)
tutor_details = models.CharField(verbose_name='tutor details', max_length=100)
picture = models.ImageField(verbose_name='picture', upload_to='tutor_pictures')
def __str__(self):
return self.chalkslate_user.email
# make the ins_tutor and ins_student models
class InsStudent(models.Model):
institute = models.ForeignKey(ChalkSlateAdmin, on_delete=models.CASCADE)
student = models.OneToOneField(Student, on_delete=models.CASCADE)
roll = models.IntegerField()
class_year = models.CharField(max_length=50)
section = models.CharField(max_length=50)
class Meta:
unique_together = ('roll', 'class_year', 'section',)
class InsTutor(models.Model):
institute = models.ForeignKey(ChalkSlateAdmin, on_delete=models.CASCADE)
tutor = models.OneToOneField(Tutor, on_delete=models.CASCADE)
class notice(models.Model):
name=models.CharField(max_length=50,null=False)
mail=models.CharField(max_length=50,null=False)
date=models.DateTimeField(null=False)
content=models.CharField(max_length=200,null=False)
per=models.CharField(max_length=50,default='no')
def __str__(self):
return self.name
For choices option, you can set a method that returns a list with what you want in it.
For instance, for a BankCard model I implemented in one of my projects, I did something similar to what you want to achieve, for the expiration_year. See below :
class BankCard(models.Model):
def _years(year):
return [(year, str(year)) for year in range(year, year + 10)]
expiration_year = models.IntegerField(choices=_years(datetime.datetime.now().year))
Maybe you could do something similar.

Categories