How to simplify phone_number field update Process in django including verification - django

Problem Description:
We have a User model where Phone number is been set as username and its unique=True,
We also have a field where to represent verified/unverified users. And we verify users by OTP verification to phone number. So now we need to add a new feature where user can replace his phone_number and then he will go to verification process and then we will change his number.
And also We need to preserve his previous number (which he already verified) until this new number is verified.
If for some reason user decides to go back without verification we delete newly added number and keeps his old number as username.
So what would be the best way to approach this problem
What I'm considering to have another Model linked with ForeignKey to User
class AlternativeNumber(models.Model):
user = User() # forignkey
number = Charfield
is_verfied = BooleanField
So we will be verifying users number and replacing his original number from his alternativenumber_set
Do you think we can simply it more. Do you think it might have some flaws or we go with a better appraoch ? thanks.

In my opinion you can add two new fields to the User class
alternative_number = CharField
is_aletrnative_verfied = BooleanField
which is really similar to your approach, but it is simpler because all the data is in one table. if a user did not verify his number then you can create a cron job that checks if the is_aletrnative_verfied and if it was false then delete whatever value is in the alternative_number just for the data to be consistent.
Hope you find this helpful!

Related

When to use NullBooleanField in Django

I have a button that, when clicked, should save in the database that the user has drunk water. I just wanted to check whether NullBooleanField would be the correct way to define this.
A broader question that if answered would be useful to the community is a list of optimal circumstances under which to use NullBooleanField. But I'm not asking that here. Just in case you wanted a better challenge.
Thank you in advance.
The question you need to answer to find out whether you should use the BooleanField or the NullBooleanField is actually concerning the possible states of the value of the field you want to represent in your model:
2 possible states:
user has drunk water
user has not drunk water
→ use BooleanField
3 possible states:
user has drunk water
user has not drunk water
it is not known whether the user has or has not drunk water
→ use NullBooleanField.
UPDATE:
NullBooleanField is deprecated in version 3.1. Instead use BooleanField with null=True.
Django 2.1 introduced null=True for BooleanField. Using NullBooleanField is now discouraged.
So use, x = BooleanField(null=True) instead of x = NullBooleanField()
Here's a simple use case: If you only need to record the "Yes" or "No" status, use Boolean without null. But if you want to have 3 conditions say, "Yes", "No", and "Don't Know", use it with null=True.
I think you should use NullBooleanField only when you have three possible choices: Unknown, Yes (True) and No (False).
In your case you have only two possible values - Yes (user has drunk water) and No (user has NOT drunk water) so a BooleanField would be better.
One more reason to use a BooleanField in your case is because the default form widget for this field is a CheckboxInput (docs), while the default form widget for a NullBooleanField is a NullBooleanSelect (docs). And since you use a checkbox, a BooleanField would do the job better.
Take advantage of the NULL properties
I use it quite often when I need to enforce some specific constrains in my data, but allow some others. Multiple NULL values can coexist in a column defined UNIQUE. Let's take an address model implementation as an example:
The business rules are:
A user can have up to 1 billing address
A user can have multiple shipping addresses
One way to implement that is by making a single address table with a foreign key to the user and an extra flag that indicates if that address is a billing address or not:
class Address(models.Model):
... # <- address fields
user = models.ForeignKey(User, on_delete=models.CASCADE)
billing_address = models.NullBooleanField(default=None)
You can now simply enforce the business rules at a database level by making user and billing_address unique together.:
class Meta:
constraints = [
models.UniqueConstraint(
fields=['user', 'billing_address'],
name='ensure single billing address'
)
]
The trick to make this work is that the billing_address must be True when the address is a billing address but it should be None (instead of False) when the address is a shipping address.
You can further enforce the validation by adding another constraint to make sure that no False values are added. But this is usually not necessary and could be done at the application level:
class Meta:
constraints = [
models.UniqueConstraint(
fields=['user', 'billing_address'],
name='ensure single billing address'
),
models.CheckConstraint(
check=~Q(billing_address=False),
name='no False allowed'
)
]
Biggest advantage of using NullBooleanField for PostgreSQL database is your table won't be re-written, any new field without null=True will cause a table re-write, it's fine for small tables but could take a significant amount of time and resource for large tables and you won't be able to write to your table during a re-write

Django filter on two fields of the same foreign key object

I have a database schema similar to this:
class User(models.Model):
… (Some fields irrelevant for this query)
class UserNotifiy(models.Model):
user = models.ForeignKey(User)
target = models.ForeignKey(<Some other Model>)
notification_level = models.SmallPositivIntegerField(choices=(1,2,3))
Now I want to query for all Users that have a UserNotify object for a specific target and at least a specific notification level (e.g. 2).
If I do something like this:
User.objects.filter(usernotify__target=desired_target,
usernotify__notification_level__gte=2)
I get all Users that have a UserNotify object for the specified target and at least one UserNotify object with a notification_level greater or equal to 2. These two UserNotify objects, however, do not have to be identical.
I am aware that I can do something like this:
user_ids = UserNotify.objects.filter(target=desired_target,
notification_level__gte=2).values_list('user_id', flat=True)
users = User.objects.filter(id__in=user_ids).distinct()
But this seems a step too much for me and I believe it executes two queries.
Is there a way to solve my problem with a single query?
Actually I don't see how you can run the first query, given that usernotify is not a valid field name for User.
You should start from UserNotify as you did in your second example:
UserNotify.objects.filter(
target=desired_target,
notification_level__gte=2
).select_related('user').values('user').distinct()
I've been looking for this behaviour but I've never found a better way than the one you describe (creating a query for user ids and inject it in a User query). Note this is not bad since if your database support subqueries, your code should fire only one request composed by a query and a subquery.
However, if you just need a particular field from the User objects (for example first_name), you may try
qs = (UserNotify.objects
.filter(target=desired_target, notification_level__gte=2)
.values_list('user_id', 'user__first_name')
.order_by('user_id')
.distinct('user_id')
)
I am not sure if I understood your question, but:
class User(models.Model):
… (Some fields irrelevant for this query)
class UserNotifiy(models.Model):
user = models.ForeignKey(User, related_name="notifications")
target = models.ForeignKey(<Some other Model>)
notification_level = models.SmallPositivIntegerField(choices=(1,2,3))
Then
users = User.objects.select_related('notifications').filter(notifications__target=desired_target,
notifications__notification_level__gte=2).distinct('id')
for user in users:
notifications = [x for x in user.notifications.all()]
I don't have my vagrant box handy now, but I believe this should work.

Default django field value when creating from record that is being created

I have People app. I have Person model that has username field. When I add a person and that person does not have an account yet, I click the little plus sign next to the username field and I'm presented with a New User form.
That is okay.
Now I want to create users in this manner: The username should be in a form of {firstname}.{lastname}[.{serial}] fo that John Smith will have username john.smith and another John Smith would be john.smith.1.
How can I pass the firstname and lastname to the new user form? Also: How to check if there's another user with that username and append the serial number to it?
Thank you
If the first and last names are being entered (but not yet submitted) on the Person form, and you want those values to show up in the new user form view, then you'll have to:
a) use JavaScript to grab the first name and last name when the user hits that add button (the "plus button"), then
b) pass those values into a custom view that dynamically creates the {firstname}.{lastname}.[serial], then
c) assign that value to form.fields['username'].initial
When the user sees the next form, they will have the value you entered pre-populated. This logic should probably go in a custom form method (whichever form manages the username).
This strategy brings up other issues you'll have to address:
1) What if the user changes the First and Last name in the original Person form, before submitting? (#2 below might solve this)
2) Every time the first or last name are changed, you'll have to update the username on the other model
Other things to consider:
You could do this using a custom user model (docs, tutorial), and put those first and last names in the same model, which could eliminate the custom view creation, allow you to create the username dynamically in one page, and make it easier to sync them.

Django Models - Prepare the data for the database

I have a form that askes for a phone number. I need to make sure that only digits [0-9] get saved in the database.
In the Django documentation it says:
What happens when you save?
3) Prepare the data for the database. Each field is asked to provide its current value in a data type that can be written to the database.
How does this happen? Or more specifically, how can I make sure this is cleaned? I know that I can just override the models save method, but it seems like there is a better way and I'm just not sure how to do it.
I guess I could write a custom field for it, but that seems like overkill here.
Also, I realize that I can put the validation on the form, but it really feels like stripping out the characters belongs on the model.
Your question specifically about point 3 is a little different from "cleaning" in the way django uses the term.
3) Prepare the data for the database. Each field is asked to provide its current value in a data type that can be written to the database.
Point 3 is about converting the python object values to one suitable for a database. Specifically, this is done in Field.get_prep_value and Field.get_db_prep_value
https://docs.djangoproject.com/en/dev/howto/custom-model-fields/#django.db.models.Field.get_prep_value
It's the opposite of to_python which takes a DB value and converts it to a python object.
As for ensuring only digits 0-9 get stored, that would be done in a Fields clean method (subclass IntegerField), form clean method, form clean_FIELDNAME method, or model clean.
You can add a custom Form Cleaning method to your objects model - take a look at this article https://docs.djangoproject.com/en/dev/ref/forms/validation/#form-field-default-cleaning
Look at "Cleaning a specific field attribute"
use django model form + custom form field cleaning
Below is a quick example of what you might be looking for, where MyModel is the model containing the phone number field, which I named it tel here.
import re
class MyForm(ModelForm):
class Meta:
model = MyModel
def clean_tel(self):
tel = self.cleaned_data.get('tel', '') # this is from user input
# use regular expression to check if tel contains only digits; you might wanna enhance the regular expression to restrict the tel number to have certain number of digits.
result = re.match(r'\d+', tel)
if result:
return tel # tel is clean so return it
else:
raise ValidationError("Phone number contains invalid character.")

Multi field and computed value

I need a solution for this problem:
I would like to have MultiField widget for a "value" field. This field should allow to input two values (two input widgets), and user should choose which of these values is to be saved (two checkbox widgets). However, one of these values must be recalculated with respect to other field's value.
I've taken this approach:
a MultiValueField with 4 fields:
class PriceDetailField(MultiValueField):
use_net = BooleanField(required=False)
net_value = DecimalField(required=False, decimal_places=2)
use_gross = BooleanField(required=False)
gross_value = DecimalField(required=False, decimal_places=2)
a MultiWidget:
class PriceDetailWidget(MultiWidget):
use_net = CheckboxInput()
net_value_widget = TextInput()
use_gross = CheckboxInput()
gross_value_widget = TextInput()
and a custom Form...
class PriceModelForm(ModelForm):
value = PriceDetailField()
...which overrides default model form for a model:
class Price(models.Model):
value = models.DecimalField(
max_digits=19,
decimal_places=2,
default=Decimal(0),
)
However, this solution seems to be messed up. In the form, I need to input all subfields (the MultiValueField subfields), otherwise "Please enter value" error appears (even though those fields are marked as required=False). Also,
I must recalculate the mentioned value upon a save, having returned a tuple from the field with information which checkbox was checked and the corresponding text value, then replace the tuple with the decimal value in clean_value method of form (also, saving the checkboxes state in temporary fields....). I think such a design is very weak.
The form should work both on its own and as inline (this means, the value of the field which is used to calculate the returned value can or cannot change during save).
Is such a thing even possible?
And the root of the problem: I want to store prices of items as net prices, but I would like to allow users to input them as net or gross prices, and then recalculate gross price to net price with respect to product's VAT tax level (since VAT is assigned to product or service, not to the price). A product can have many prices, so a price is backlinked to the product by a foreign key.
Cheers,
Tomek
The feature you're looking for on a MultiValueField (allowing the sub-fields to be required or not individually) is logged as a feature request on Django.
You could probably fix this for now by subclassing MultiValueField with a rewritten clean method based on the original and following suggestions from the bug report. You're not going to have a lot of the original MultiValueField left by that point though, and if you do, you should submit your patch to Django and put a note on the bug.
Ignoring for a moment the requiredness thing, have you written a compress method on your PriceDetailField? What does it look like? This is where you should be doing the work to turn your four sub-fields into a single field to save.