I am working on a project where I have a class called Investment. Basically, a user can can have many investments, and an investment can consist of many users. As such my models.py looks like this:
# Not sure if this class is relevant to my question
class UserProfile(models.Model):
user = models.OneToOneField(User)
first_name = models.CharField(max_length=24)
last_name = models.CharField(max_length=24)
phone_number = models.CharField(max_length=12)
account_type = models.CharField(max_length=55, choices=ACCOUNT_TYPES)
def __str__(self):
return self.user.username
class Investment(models.Model):
user = models.ForeignKey(User)
offered_fund = models.ForeignKey(OfferedFunds)
amount = models.IntegerField(default=0)
purchase_date = models.DateTimeField(auto_now_add=True)
def __int__(self):
return self.offered_fund.fund_name.name
and my forms.py:
class InvestmentForm(forms.ModelForm):
class Meta:
model = Investment
fields = ('offered_fund','amount')
exclude = ['user']
finally, my views.py:
if form.is_valid():
instance = form.save(commit=False)
instance.user = request.user
instance.save()
So the way it works when deployed is the user selects an offered_fund, and enters an amount then hits submit. When I do this once, it registers and I can see it in the Django Administration page. However, the second time I try to do it (selecting the same or a different offered_fund) I get the error:
UNIQUE constraint failed: users_investment.user_id
Any idea on how I can solve this? Thanks.
You're using a ForeignKey relationship which only allows one User object for each Investment.
You should be using a ManyToManyField which allows more than one User per Investment.
Related
I am doing an online classroom project in Django where I created a model named create_course which is accessible by teachers. Now I am trying to design this as the teacher who creates a class only he can see this after login another teacher shouldn't see his classes and how to add students into that particular class I created
the course model
class course(models.Model):
course_name = models.CharField(max_length=200)
course_id = models.CharField(max_length=10)
course_sec = models.IntegerField()
classroom_id = models.CharField(max_length=50,unique=True)
views.py
def teacher_view(request, *args, **kwargs):
form = add_course(request.POST or None)
context = {}
if form.is_valid():
form.save()
return HttpResponse("Class Created Sucessfully")
context['add_courses'] = form
return render(request, 'teacherview.html', context)
forms.py
from django import forms
from .models import course
class add_course(forms.ModelForm):
class Meta:
model = course
fields = ('course_name', 'course_id', 'course_sec', 'classroom_id')
Add one more field in course model that establish relationship with User model. Hence you can get the details about the teacher who has created course.
from django.contrib.auth.models import User
class course(models.Model):
course_name = models.CharField(max_length=200)
course_id = models.CharField(max_length=10)
course_sec = models.IntegerField()
classroom_id = models.CharField(max_length=50,unique=True)
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
In your view function, you can check whether logged in user is same as the created of the requested course.
def teacher_view(request, *args, **kwargs):
# since this is course specific view, you will be passing an identiier or pk of the course as an argument to this function.
course_obj = Course.objects.get(id="identifier")
if request.user == course_obj.created_by:
# logged in user is same as the creator of the course
else:
# redirect
I would prefer creating permissions and having those in specific models. You can give it a try with that too. Let me know, if it doesn't work.
Here is the scenario I am working on: I have django app that creates records which I call sessions:
blog.models.py
class Session(models.Model):
uid = models.CharField(max_length=50, blank=True)
cid = models.CharField(max_length=50, blank=True)
action_type = models.CharField(max_length=50, blank=True)
action_name = models.CharField(max_length=50, blank=True)
action_value = models.CharField(max_length=50, blank=True)
session_date = models.DateTimeField(auto_now_add=True)
client = models.CharField(max_length=50, blank=True)
I have a dashboard page to show charts and a database page to show the records as a table:
blog.urls.py
path('', auth_views.LoginView.as_view(template_name='users/login.html'), name='blog-home'),
path('<str:username>/dashboard/', views.dashboard and DashboardListView.as_view(), name='blog-dashboard'),
path('<str:username>/database/', views.database and SessionListView.as_view(), name='blog-database'),
So when you log in, my SessionListView.as_view() goes through the whole database and displays only those records where the Session.client == the url's 'username' value.
Example: when user: DummyCo logs in (www.website.com/DummyCo/database/) they see only Session records where the Session.client field is 'DummyCo.' This has worked out great so far.
But here is the problem: I now need to provide multiple logins to users to see the same dashboard and database page.
Example: jim#DummyCo.com and amy#DummyCo.com both need to see the DummyCo records, but if I provided them with their own logins then their username's in the url would not match and thus the DummyCo records would not show. I thought using the built-in django Groups would be a solution but that seems to only help with authentication and permissions on the backend. I also extended my user model with a Profile model:
users/models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
group = models.ForeignKey(Group, blank=True, null=True, default=None, on_delete=models.SET_DEFAULT)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
user_client = models.CharField(max_length=50, blank=True, null=True, default=None)
def __str__(self):
return f'{self.user.username} Profile'
I made the user_client model field to try and connect the Profile (and thus User) with the Session.client field: instead of <str:username>/database/ I thought i'd be able to use <str:client_user>/database/ and simply fill that field with 'DummyCo' on both Jim and Amy's profile to give them access to the records.
I read in a couple of places that the key to handling this problem is to switch the user model from one-to-one to many-to-one type early or before i build out the app. Unfortunately I have already put a ton of work into this project. I also read that I should look at the built-in User model as more of an account and less of a user. So is there a simple way to give multiple users access to one User/account?
Also, here is the views:
blog/views.py
class SessionListView(LoginRequiredMixin, ListView):
model = Session, Profile
template_name = 'blog/database.html'
context_object_name = 'sessions'
ordering = ['-session_date']
paginate_by = 25
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Session.objects.filter(client=user).order_by('-session_date')
def get_context_data(self, **kwargs):
user = get_object_or_404(User, username=self.kwargs.get('username'))
context = super().get_context_data(**kwargs)
context['distinct_campaigns'] = Session.objects.filter(client=user).values('cid').distinct().order_by('cid')
context['distinct_action_types'] = Session.objects.filter(client=user)\
.values('action_type')\
.distinct().order_by('action_type')
return context
# login_required()
def database(request):
context = {
'sessions': Session.objects.all()
}
return render(request, 'blog/database.html', context, {'title': 'Database'})
Okay I figured out a solution:
I thought I needed to do some trickery on the html file within the for loop showing my query set sessions but it turns out that can be adjusted in my views.py file. Before this update my views.py looked like this:
class SessionListView(LoginRequiredMixin, ListView):
model = Session, Profile
template_name = 'blog/database.html'
context_object_name = 'sessions'
ordering = ['-session_date']
paginate_by = 25
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Session.objects.filter(client=user).order_by('-session_date')
def get_context_data(self, **kwargs):
user = get_object_or_404(User, username=self.kwargs.get('username'))
context = super().get_context_data(**kwargs)
context['distinct_campaigns'] = Session.objects.filter(client=user).values('cid').distinct().order_by('cid')
context['distinct_action_types'] = Session.objects.filter(client=user)\
.values('action_type')\
.distinct().order_by('action_type')
return context
I realized the def get_queryset(self) was grabbing the logged-in username, then reviewing the full database and adding all records with the same session.client value as the value of the logged in user (i.e. DummyCo). So to make this work for a user like 'DummyCo_Sally', I changed the logic in that def like so:
class SessionListView(LoginRequiredMixin, ListView):
# gets the actual user (i.e. DummyCo_Sally)
user = get_object_or_404(User, username=self.kwargs.get('username'))
# turns user to a string
user_string = str(user)
# designates the _ as the separator
sep = '_'
# strips off _ and everything after it
stripped_user = user_string.split(sep, 1)[0]
# establishes the queryset as 'DummyCo' even though 'DummyCo_sally' is logged in
return Session.objects.filter(client=stripped_user).order_by('-session_date')
I doubt this method is the best way of handling multiple users seeing one umbrella data set, but it did the trick for me. This method also likely creates a security risk for applications that have public-facing user registration. But it did the trick for me.
I am working on developing a Trello-like website with Django Rest Framework.
I want to add selected users to BoardAccess model, a through table for User model and Board model, two of which are in Many to Many relationship. Being added to BoardAccess table will mean that the respective users will be having access to matching boards.
Models.py
class Board(models.Model):
name = models.CharField(max_length=50)
access_granted = models.ManyToManyField(User, through='BoardAccess', related_name='access_boards')
team = models.ForeignKey(Team, on_delete=models.CASCADE) # a team can have many boards
class BoardAccess(models.Model):
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
board = models.ForeignKey('Board', on_delete=models.CASCADE)
For User, I am currently using Django's default Auth User model and extending it with a Profile model via OneToOne Field.
Serializers.py
class BoardAccessSerializer(serializers.ModelSerializer):
members = serializers.SerializerMethodField()
added_users = # ???
new_name = serializers.CharField(
write_only=True, required=False, source='name') # in case of requests for renaming the board
def get_members(self, instance):
members = User.objects.filter(profile__team=instance.team)
return UserBoardSerializer(members, many=True).data
I would like to know what field / relations / another serializer should be assigned to added_users, which I think should be write_only=True, in order to successfully de-serialize input from the client-side containing primary keys of selected users.
get_members() method is used to first display information of all team members, from which a client will select users to be added to the board.
Views.py
class BoardAccessRetrieveUpdateAPIView(generics.RetrieveUpdateAPIView):
serializer_class = BoardAccessSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
team_id = self.kwargs.get('team_id')
team = get_object_or_404(Team, id=team_id)
queryset = Board.objects.select_related(
'team').prefetch_related(
'access_granted').filter(team=team)
return queryset
I am new to DRF, so there may be a lot of points to be improved from the above. I would really appreciate every help!!
You can override the update method in your serializer, and get the user ids from the client side from initial_data
def update(self, instance, validated_data):
// do the actions on create
users = self.initial_data.get('users')
instance.access_granted.add(*users)
instance.save()
return instance
Also when using ModelSerializer have to add Meta class:
class BoardAccessSerializer(serializers.ModelSerializer):
class Meta:
model = Board
fields = "__all__"
i have some fields in my django models.
class Product(models.Model):
user=models.ForeignKey(User)
product_name = models.CharField(max_length=50)
product_cost = models.IntegerField(default=0,null=True, blank=True)
product_description = models.TextField(null=True, blank=True)
quantity = models.IntegerField(default=0,null=True, blank=True)
product_image = models.FileField(upload_to='images/',blank=True,null=True,)
coupon_code = models.CharField(max_length=50)
time = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.product_name or u''
when i use form to save all the data from front end in my database i can do this.
class DocumentForm(forms.ModelForm):
class Meta:
model = Product
fields = ('user','product_name','product_image','product_cost','product_description','product_description','coupon_code')
Problem is this i don't want to allow user to fill user data from front-end.when user save data it save request.user to user.
I am new to work with forms so facing some issues.
Please help me how can i do this.
Thanks in advance.
In fields you have to remove user:
so it will look like this:
class DocumentForm(forms.ModelForm):
class Meta:
model = Product
fields = ('product_name','product_image','product_cost','product_description','product_description','coupon_code')
and in your views.py when you save the data from the form you have write something like this:
user = request.user
in this case user will be saved if he is authenticated.
If you want that not authenticated user could fill the form too, you have to change in your model.py Product class from:
user=models.ForeignKey(User)
to:
user=models.ForeignKey(User, null=True)
then not authenticated user will be NULL.
I want to expand my User Model with a UserProfile model. This UserProfile model includes a ForeignKey Field. In the form, I would like to use a ModelChoiceField to pre-populate this form field.
Whenever I submit the form, I get
ValueError at /accounts/register/
Cannot assign "'13'": "UserProfile.course" must be a "Course" instance.
Any help would be appreciated!
My Code:
models.py
class Course(models.Model):
course_accid = models.CharField(max_length=10)
def __str__(self):
return self.course_accid
class UserProfile(models.Model):
# This line is required. Links UserProfile to a User model instance.
user = models.OneToOneField(User)
website = models.URLField(blank=True)
picture = models.ImageField(upload_to='profile_images', blank=True)
course = models.ForeignKey(Course)
def __unicode__(self):
return self.user.username
def user_registered_callback(sender, user, request, **kwargs):
profile = UserProfile(user = user)
profile.website = request.POST["website"]
profile.course = Course.objects.get(pk=request.POST["course"]),
profile.save()
forms.py
class RegistrationForm(RegistrationForm):
course = forms.ModelChoiceField(queryset=Course.objects.all())
website = forms.URLField()
So, the problem that's occurring is that course needs to be set to a course instance with a step before, on forms.py, before it's a ModelChoiceField. The reason why is because querying it, like you're doing with queryset is really just searching for a string that matches, not the actual object.
If you break it up into two steps,
class = [some_method_for_getting_a_class_object]
UserProfile.class = class
Then it should get rid of that error.