I have been searching for best practice, but I did not found it. I was even unable to find solution I need used by anyone else.
I need to generate username of the user based on his other data (first name & last name), optionally appending integer at the end, until I get the unique username.
I strongly prefer doing that in model. Is there some standard way to do that? Or is it only appropriate in forms? I have been researching overloading of various User model methods, as well as signals, and did not find any proper place I could add it.
One possible solution could be through pre_save signal.
def my_callback(sender, **kwargs):
obj = kwargs['instance']
if not obj.id:
username = get_unique_username(obj) # method that combines first name and last name then query on User model, if record found, will append integer 1 and then query again, until found unique username
obj.username = username
pre_save.connect(my_callback, sender=User)
Related
i am building an application that has a model with three fields Company,Name, position. in the same model i want to have company name as one field while the user can add name and positions for multiple candidates. the reason am trying to do that is because i didnt find any proper way to set automatically select the foreign key based on the company name entered since foreign key is a drop down list and couldnt figure out the way to make foreign key field equal to company name entered.
appreciate help and suggestions if any for the approach i have in mind.
You need two forms (or more usefully one form and one formset). Use form prefixes to make them distinguishable. Pass both to the template, say as selectform and candidate_formset and in the template, use
{{selectform}}
{{candidate_formset}}
The first is a company-select form. It might, for example, be
class CompanySelectForm(forms.ModelForm):
class Meta:
model = Candidate
fields = ['company']
The second is a form, or probably a formset, for entering one, or (via a formset) as many candidates as there are to be entered. It will look like
class CandidateForm(forms.ModelForm):
class Meta:
model = Candidate
fields = ['name','position']
Now, you use commit=False (docs) to create objects but not save them. First, process CandidateSelectForm, which will give you a Candidate object with a valid company instance, but not save it. Then process the formset of CandidateForm, again with commit=False, which will give you a list of candidate instances with no company, again unsaved. Finally for each candidate in this list, set the company field of every candidate to the one on the object retrieved by CandidateSelectForm and save it.
It will probably be easier to write a plain view function, than messing around with method overrides trying to get the class-based views to process two forms this way.
Edit - added on request.
The view could be modelled on this one in the Django doc. I've made the obvious changes in line with the earlier part of the answer, but it's probably full of errors and I'm not going to debug it further here
def get_name(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create form instances and populate with data from the request:
cs_form = CompanySelectForm(request.POST, prefix="cs")
cand_form = CandidateSelectForm( request.POST, prefix="cand")
# check whether it's valid:
if cs_form.is_valid() and cand_form.is_valid():
selector = cs_form.save(commit=False)
candidate = cand_form(commit=False)
candidate.company = selector.company
candidate.save()
return HttpResponseRedirect('/thanks/')
# if a GET (or any other method) we'll create a blank form
else:
cs_form = CompanySelectForm( prefix='cs')
cand_form = CandidateSelectForm( prefix='cand')
return render(request, 'name.html', {
"select_form": cs_form,
"candidate_form": cand_form,
})
Once you have got this working for a single candidate, you can progress to turning candidate_form into a formset, documented here which will let you enter any number of candidates to be attached to the one selected company.
I'm using Django Rest Framework to handle token authentication for a mobile application for a school. In particular, when a student logs in, the mobile application sends a token to my Django backend, which then combines data from its database and some external data from another source. I found it easiest to use a generic RetrieveAPIView to accomplish what I needed.
My code is working, and my main question is around the url. For most retrievals, we usually have the primary key as well (e.g. /students/SOME-ID), but in this case, I'm using the token to retrieve the user rather than the primary key. In fact, if SOME-ID passed in was different from the Token, the user associated with the Token would be returned anyway (which seems kinda strange).
I'm wondering whether it is better to have my url route be just (/students) instead though this seems to be a list rather than a retrieve operation.
WHAT I HAVE NOW
http://localhost:8000/api/v1/revision/students/1
IS THIS BETTER
http://localhost:8000/api/v1/revision/students/
CODE
class StudentView(generics.RetrieveAPIView):
model = Student
serializer_class = StudentSerializer
# combines data from both current and legacy database
def retrieve(self, request, pk=None):
obj = get_object_or_404(Student, user=request.user)
# KIV -> unsure if this is the best way to combine data from legacy and current database
# or should it be done in the serializer
data = StudentSerializer(obj).data
# combines existing data stored in database with legacy data from database
legacy_data = SOME_EXTERNAL_API_SERVICE.get_student_info(obj)
data['avatar'] = legacy_data['avatar']
data['coins'] = legacy_data['coins']
return Response(data)
I would definitely not use /students/id/ with the behaviour you're describing: This URL should always return the student with the given id of error (depending on whether the user fetching this resource is allowed to do so). You might want to use this URL for admins to view students in the future.
And for the same reason, I wouldn't use /students/ because I'd expect it to return a list of all students, or at least the list of all students the particular logged in user is allowed to see. This might fit your purpose now (where the logged in user can only see himself), but maybe not in the future if you create new roles that can view more students.
There are two approaches here:
Either you treat this as a filter on all the students: /students/?current=true which I personally find ugly because you're not actually filtering on the total set of students.
Or you treat this as a special case: /students/current using a special keyword for fetching this one specific student.
I would choose the latter one because it is more descriptive and easier to understand when looking at the API. Note of course that id can never be 'current' in this case, which is why some people discourage this kind of special resource queries and opt for the first option.
Definitely, the url http://localhost:8000/api/v1/revision/students/ looks better.
But you don't need to write this in a RetrieveAPIView, you could always do this in base APIView,
class StudentView(APIView):
def get(self, request, *args, **kwargs):
obj = get_object_or_404(Student, user=request.user)
data = StudentSerializer(obj).data
legacy_data = SOME_EXTERNAL_API_SERVICE.get_student_info(obj)
data['avatar'] = legacy_data['avatar']
data['coins'] = legacy_data['coins']
return Response(data)
By using like this, you can avoid the extra pk keyword argument from your url.
I'm a newbie with django and working on a project where I need to display registered user's last visited pages on their profile page. I have achieved that within my extended user class by adding a new many2many field to my main object which I want to keep history for. In my view, whenever a member makes a request I add the object to member's history. But this doesn't give me the result that I want. Items are not ordered and if user is not logged in it gives User DoesNotExist error. I know there is a better way then this but I could't find it. Probably I'm not on the correct way. I appreciate any help or ideas.
class myObjectView(View):
model = myObject
template_name = 'app/myobject_detail.html'
def get(self, request, **kwargs):
cat = Category.objects.all()
sec = Section.objects.all()
self.item = myObject.objects.get(slug = self.kwargs[u'slug'])
user = User.objects.get(username=request.user.username)
if user.is_authenticated():
if self.item in user.member.history.all():
user.member.history.remove(self.item)
user.member.history.add(self.item)
user.save()
else:
user.member.history.add(self.item)
user.save()
Your approach has drawbacks but is not a bad one if you need long term persistance.
You could easily add an ordering field in your m2m through table (look at docs) to add some sense of ordering. You could also order your m2m through table by its PK, as large PK values would mean newer entries given your current code of removing items and adding them again.
ordered_item_history = (user.member.history.through.objects
.filter(user=user, myObject=self.item)
.order_by('pk').values_list('myObject', flat=True))
That said the easiest way to do something like this is in the session.
request.session.setdefault('history', []).append(myObj)
request.session.modified = True
Now in any view, you can access this ever-growing list of object history via request.session['history']. Modify as necessary to eliminate duplicates.
Right now I'm using Django's built in admin system to manage users, to which I've attached a profile to contain additional data using the following:
class Profile(models.Model):
user = models.OneToOneField(User, editable = False)
# Data fields here...
As it stands the User and Profile pk (and accordingly id number) will be the same if and only if the profile is created right after the user is created. I could guarantee that this would be the case during the registration process, and while that would cover most uses, creating users with the admin interface could cause mismatched ids to occur. Thus this does not seem like a very robust way to solve this problem and I'd like to hardcode the pk's to be the same. I'm not sure how to do this.
I thought the following would work:
profile_id = models.IntegerField(default=user.pk, editable = False,
primary_key = True)
But it gives me the error:
AttributeError: 'OneToOneField' has no attribute 'pk'
What's the best way to guarantee that the profile and user have the same pk? Note: I'd really rather not deal with extending the base user model as using the OneToOneField to link the two seems to be sufficient for all my needs.
Thanks!
[edit]
My reasoning for asking the question:
My immediate problem was that I wanted a dictionary of values of the User's Profile, which I was retrieving usingprofile_values = Profile.objects.filter(pk=user.id).values()[0]. This highlighted the bug, and I "hacked" around it last night using pk=user.profile.id instead. In the light of the morning this does not seem like such a terrible hack. However, it seems like having pk discrepancies could lead to quiet and hard to catch bugs down the line, and thus forcing them to match up would be a Good Idea. But I'm new to Django so I'd entirely accept that it is, in fact, never a problem if you're writing your code correctly. That said, for almost academic reasons, I'd be curious to see how this might be solved.
[/edit]
Like you already agree that it was never a problem because we have a OneToOne mapping between the two models.
So when you need to get the profile obj corresponding to a User:
profile_values = Profile.objects.get(user_id=user)
assuming,
class Profile(models.Model):
user = models.OneToOneField(User)
...
If your column name is not user, then use the corresponding name in get query.
Still if you are curious as to how to achieve same pk for both models, then we can set a signal on every save of User model. See the documentation.
def create_profile(sender, **kwargs):
if kwargs["created"]:
p = Profile(user=kwargs["instance"], ...)
p.save()
django.db.models.signals.post_save.connect(create_profile, sender=User)
create_profile() will be called every time any User object is saved.
In this function, we create Profile object only if a new User instance has been created.
If we start from blank slate, then I think this will always make sure that a Profile exists for every User and is created right after User was created; which in turn will give same pk for both models.
pk is a parameter in a filter() query, but not a field name. You probably want to use user.id.
is there another REGEX way (or another way) to ensure that a model class field would be unique? (it is not a key, or at least not declared as a key, is shoulb be a simple CharField)
Thanks
The normal way to make a single field unique is to use the unique argument to the field constructor.
If you need to make this unique on more than one field, have a look at:
unique-together
There are two ways of doing so.
The first is to mark the entire column as unique. For example:
product_name = models.Charfield(max_length=10, unique=True)
This method is good when you want your entire column to be inherently unique regardless of the situation. This can be used for username, id, key etc.
However, if the column cannot be inherently unique but it has to be unique in relation to others, you have to use the manual way.
from django.core.exceptions import ObjectDoesNotExist
try:
n = WishList.objects.get(user=sample_user, product=sample_product)
# already exists
return False
except ObjectDoesNotExist:
# does not exist
wish_list = WishList(user=sample_user, product=sample_product)
wish_list.save()
return True
Take this as an example. You have a wish list which none of the items can be unique. A single user can have many products and a single product can be in the wish list of many users. However, a single user cannot add one particular product to his or her wish list more than once. And this is where unique=True cannot be used and we have to use try and except