Trouble getting object in Django's models - django

I have a user submission form, and the user supplies his name and email. Each email is associated to a network (by an admin, prior to user registration), and based upon a user's email he will be assigned to that network.
Here is what the models.py looks like --
class Network(models.Model):
network = models.CharField(max_length=50)
location = models.CharField(max_length=50)
class EmailList(models.Model):
email = models.EmailField(blank=True)
network = models.CharField(max_length=50)
class User(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField(max_length=50)
network = models.ForeignKey(Network)
And in the views.py this is what I am using to try and insert the record into the database --
User.objects.create(
name = cd['name']
email=cd['email'],
network= EmailList.objects.filter(email=request.POST.get('email'))['network'])
However, I am getting an exception TypeError from the network= line. What should the syntax be here to 'pull' and insert the network associated with the email into the database? What am I doing incorrectly
Update
Here is the code I used in views.py to get it working.
email_list = EmailList.objects.get(email=cd['email'])
network= Network.objects.get(network=email_list.network)
User.objects.create(
name=cd['name'],
email=cd['email'],
network=network)
When I tried setting the variable email = cd['email'] and then defining the email_list using that variable like so -- email_list = EmailList.objects.get(email=email), it would raise an exception saying Queryset not found in EmailList and would pass a unicode string.
Why does defining the variable before passing it in this case create a unicode string, whereas passing the expression directly in does not?

Once again, you shouldn't be using the post data directly.
Filter returns a queryset not an instance. That looks like what is causing your problem.
Also you need to get a network instance instead of a string to set to User.network:
if email for EmailList and network for Network models are unique you can do the following, if not and there are multiple entries using get will raise an Error.
name = cd.get('name')
email = cd.get('email')
email_list = EmailList.objects.get(email=email)
network = Network.objects.get(network=email_list.network)
User.object.create(name=name, email=email, network=network)

Related

How to filter the results of a query passing a string as a field

I need to append the mail_d_list only if the flag is set in the model.
I am calling this function with the airport_code already coming in from the user. Now I would like to add the user to the email list if they have the flag selected for the user or not.
Each user in the model has six boolean flags, one flag for each report possible. The text for the flag is in the middle.
I have tried .get() and .filter()
Models.py
class Aerodrome(models.Model):
''' Aerodrome model for storing three-letter airport codes iata,
airport description and the database partition informtion. '''
iata = models.CharField(max_length=3, primary_key=True)
customer = models.CharField(max_length=5)
name = models.CharField(max_length=100)
partition_code = models.CharField(max_length=6)
class DistributionList(models.Model):
''' List of all email addresses that are to receive
automated emails from the system '''
email = models.CharField(max_length=40, primary_key=True)
name = models.CharField(max_length=40)
receive_emails = models.BooleanField()
receives_report = models.ManyToManyField(Aerodrome)
script.py
for user in DistributionList.objects.filter(receives_report__iata=airport_code):
mail_d_list.append(user.email)
This is definitely the wrong approach.
You already have the aerodromes defined in a separate model. You should define a ManyToManyField as a relationship between them, rather than dynamically defining fields on your DistributionList model. Then your script can filter by that relationship.

Django - Update field values in extended user model after a request is performed

My project is a social networking site that can send requests to friends and make friends.
I have extended django's existing user model using oneToone field .
So far i've been able to do the above said thing but when ever a user accepts the request , Both the user who sent request and also the user accepted it must increment a value in their extended user model which stores the value of their friends count .
I'm facing difficulties trying to solve this .
I've also used signals but it doesn't work .
Here is my code:
models.py:
class Friends(models.Model):
"""Model for saving relationship of user and friends"""
request_id = models.ForeignKey(User,on_delete=models.CASCADE,related_name='current_user')
friend_id = models.ForeignKey(User,on_delete=models.CASCADE,related_name='user_friend')
created_on = models.DateTimeField(auto_now_add=True,auto_now=False)
class Meta:
verbose_name_plural = "Friends"
def __str__(self):
return str(self.friend_id)
class Profile(models.Model):
user = models.OneToOneField(User,related_name='profile',on_delete=models.CASCADE)
location = models.CharField(max_length=30,blank=True)
friends_count = models.PositiveIntegerField(default=0)
profile_pic = models.ImageField(upload_to='profile_pics/',blank=True,null=True)
def natural_key(self):
return (self.user.username,)
signals.py:
#receiver(post_save,sender=Friends)
def update_friends_count(sender,instance,created,**kwargs):
if created:
user_profile = Profile(user = instance.request_id)
user_profile.friends_count=F('friends_count')+1
user_profile.save()
Any help would be greatly appreciated!!!!! Thank you in advance!!!
You were initializing a new instance of Profile every time a friend is made. Instead you can use get_or_create to generate a profile or retrieve the one associated with that id. Next, you want to update both users so fetch the other update the counts and save.
#receiver(post_save,sender=Friends)
def update_friends_count(sender,instance,created,**kwargs):
if created:
user_profile, profile_created = Profile.objects.get_or_create(user = instance.request_id)
user_profile.friends_count=F('friends_count')+1
user_profile.save()
friend_profile, profile_created = Profile.objects.get_or_create(user = instance.friend_id)
friend_profile.friends_count=F('friends_count')+1
friend_profile.save()
With Q filter and update
Profile.objects.filter(
Q(user=instance.request_id) |
Q(user=instance.friend_id)
).update(
friends_count=F('friends_count')+1
)
This last query uses the django.db.models Q object to make an SQL OR statement to retrieve the two profile instances; that of the requested and the requester: the update call acts on the query and updates all instances in the list

How enforce filling up the user profile after first social login in django-allauth?

I've created UserProfile model in my application:
class UserProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
primary_key=True,
verbose_name=_('user'),
related_name='user_profile')
locality = models.CharField(max_length=85)
voivodship = models.CharField(max_length=20,
choices=Vovoidship.choices,
validators=[Vovoidship.validator])
postcode = models.IntegerField()
street = models.CharField(max_length=75)
building = models.SmallIntegerField()
premises = models.CharField(max_length=80)
phonenumber = PhoneNumberField(blank=True)
#staticmethod
def post_save_create(sender, instance, created, **kwargs):
if created:
profile, created = UserProfile.objects.get_or_create(user=instance)
post_save.connect(UserProfile.post_save_create, sender=User)
Now, I felt into my own trap. I don't want to loose constraints and keep the requirement in the database that address fields need to be filled up. I'm using django-allauth. While using the setting ACCOUNT_SIGNUP_FORM_CLASS = 'management.forms.SignupForm' solves the problem for traditional sign up form, if the user logs in first time using the social account I got hit by constraint violation for obvious reasons:
IntegrityError at /accounts/google/login/callback/
null value in column "postcode" violates not-null constraint
DETAIL: Failing row contains (4, , , null, , null, , ).
Hence the question, how to correctly implement the request for filling up the information for fields in the application UserProfile? I'm surprised that django-allauth doesn't have a build in handler for that the same way as ACCOUNT_SIGNUP_FORM_CLASS is done.
As I'm new to Django please assume I rather don't know something than it should be obvious. Thanks.
I think you need to:
1.- Create your custom Signup Class, for you to do the additional work
class SignupForm(forms.Form):
locality = forms.CharField(max_length=85)
voivodship = forms.CharField(max_length=20)
postcode = forms.IntegerField()
etc.
def signup(self, request, user):
# I think the profile will exist at this point based on
# your post_save_create. But if not, just create it here
if user.user_profile:
user.user_profile.locality = self.cleaned_data['locality']
user.user_profile.voivodship = self.cleaned_data['voivodship']
user.user_profile.postcode = self.cleaned_data['postcode']
...
user.user_profile.save()
2.- Set ACCOUNT_SIGNUP_FORM_CLASS = 'yourproject.yourapp.forms.SignupForm' to have allauth use your form
3.- Set SOCIALACCOUNT_AUTO_SIGNUP=False to ensure the form is presented even with social signup.
With some credits to davka I've managed to form a working solution which required creating UserProfile object inside signup() method of the SignupForm class, but because of database/model constrains it has be be filled with data during creation. The result:
class SignupForm(ModelForm):
first_name = CharField()
last_name = CharField()
class Meta:
model = UserProfile
exclude = ['user', 'phonenumber']
def signup(self, request, user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
profile, created = UserProfile.objects.get_or_create(
user=user, defaults={
'locality': self.cleaned_data['locality'],
'voivodship': self.cleaned_data['voivodship'],
'postcode': self.cleaned_data['postcode'],
'street': self.cleaned_data['street'],
'building': self.cleaned_data['building'],
'premises': self.cleaned_data['premises'],
})
if created: # This prevents saving if profile already exist
profile.save()
The solution doesn't totally fit into DRY principle, but shows the idea. Going further it could probably iterate over results matching model fields.
Two elements need to be set correctly in settings.py:
ACCOUNT_SIGNUP_FORM_CLASS = 'yourapp.forms.SignupForm' to enable this form in allauth
SOCIALACCOUNT_AUTO_SIGNUP = False this - contrary to the intuition - let the allauth display the form before finishing the signup if the user selected social sign in but don't have an account; it works safely if the account already exists (username and/or e-mail address depending on other settings) as just does't allow to finish registration (why they call it sign up?) and the user is forced to log in and link social account.

Django choices with model objects

Yes, this is an assignment, and yes, I've spent some time on it and now I need help.
My task has two models, Server and Client and they are in 1-N relationship. as noted below
# models.py
class Server(models.Model):
name = models.CharField(max_length=255, unique=True, null=False, blank=False)
maximum_clients = models.IntegerField(default=1,null=False, blank=False)
class Client(models.Model):
name = models.CharField(max_length=255, unique=True, null=False, blank=False)
active = models.BooleanField(default=True)
server = models.ForeignKey(Server)
I have created a form with ModelForm which allows me to create a new client on a given server, but the prerequisite of the task is to only offer servers which have free capacity (their maximum_clients is less than actual clients) so this is what I did
#forms.py
from django.db.models import Count
qs = Server.objects.annotate(Count('client'))
server_choices = []
for server in qs:
if server.client__count < server.maximum_clients:
server_choices.append((server,server))
class ClientForm(forms.ModelForm):
name = forms.CharField(label='Client name')
server = forms.ChoiceField(choices=server_choices)
class Meta:
model = Client
fields = ('name','server',)
This approach populates the select with the right servers based on a precondition that I mentioned. However, saving this form produces an error like Cannot assign "u'fifty'": "Client.server" must be a "Server" instance. Fifty is the name of the server with maximum_clients = 50
There is a similar form on admin screens which I also modified to show only available servers and saving there produces the same error.
This is not the right approach. Apart from the error you are seeing, you will also find that your server_choices only update when you restart the webserver, rather than doing so whenever the Server objects themselves change.
You have a foreign key, and need to select from a subset of the related objects. The correct field for that is a ModelChoiceField; this takes a queryset which you can filter in the definition. Since your filter depends on a field in the same model, you need to use an F object.
class ClientForm(forms.ModelForm):
name = forms.CharField(label='Client name')
server = forms.ModelChoiceField(
queryset=Server.objects.annotate(client_count=Count('client')).filter(client_count__lt=F('maximum_clients')
)

Filter M2M in template?

In my model, I have the following M2M field
class FamilyMember(AbstractUser):
...
email_list = models.ManyToManyField('EmailList', verbose_name="Email Lists", blank=True, null=True)
...
The EmailList table looks like this:
class EmailList(models.Model):
name = models.CharField(max_length=50, default='My List')
description = models.TextField(blank=True)
is_active = models.BooleanField(verbose_name="Active")
is_managed_by_user = models.BooleanField(verbose_name="User Managed")
In the app, the user should only see records that is_active=True and is_managed_by_user=True.
In the Admin side, the admin should be able to add a user to any/all of these groups, regardless of the is_active and is_managed_by_user flag.
What happens is that the Admin assigns a user to all of the email list records. Then, the user logs in and can only see a subset of the list (is_active=True and is_managed_by_user=True). This is expected behavior. However, what comes next is not.
The user deselects an email list item and then saves the record. Since M2M_Save first clears all of the m2m records before it calls save() I lose all of the records that the Admin assigned to this user.
How can I keep those? I've tried creating multiple lists and then merging them before the save, I've tried passing the entire list to the template and then hiding the ones where is_managed_by_user=False, and I just can't get anything to work.
What makes this even more tricky for me is that this is all wrapped up in a formset.
How would you go about coding this? What is the right way to do it? Do I filter out the records that the user shouldn't see in my view? If so, how do I merge those missing records before I save any changes that the user makes?
You might want to try setting up a model manager in your models.py to take care of the filtering. You can then call the filter in your views.py like so:
models.py:
class EmailListQuerySet(models.query.QuerySet):
def active(self):
return self.filter(is_active=True)
def managed_by_user(self):
return self.filter(is_managed_by_user=True)
class EmailListManager(models.Manager):
def get_queryset(self):
return EmailListQuerySet(self.model, using=self._db)
def get_active(self):
return self.get_queryset().active()
def get_all(self):
return self.get_queryset().active().managed_by_user()
class EmailList(models.Model):
name = models.CharField(max_length=50, default='My List')
description = models.TextField(blank=True)
is_active = models.BooleanField(verbose_name="Active")
is_managed_by_user = models.BooleanField(verbose_name="User Managed")
objects = EmailListManager()
views.py:
def view(request):
email = EmailList.objects.get_all()
return render(request, 'template.html', {'email': email})
Obviously there is outstanding data incorporated in my example, and you are more than welcome to change the variables/filters according to your needs. However, I hope the above can give you an idea of the possibilities you can try.
In your views you could do email = EmailList.objects.all().is_active().is_managed_by_user(), but the loading time will be longer if you have a lot of objects in your database. The model manager is preferred to save memory. Additionally, it is not reliant on what the user does, so both the admin and user interface have to talk to the model directly (keeping them in sync).
Note: The example above is typed directly into this answer and has not been validated in a text editor. I apologize if there are some syntax or typo errors.