Matching query does not exist (Django) - django

I created a forum website.
When pressing on a user profile i get and error in console that library.models.SiteUser.DoesNotExist: SiteUser matching query does not exist.
And in the browser it also displays:
DoesNotExist at /profile/1/
SiteUser matching query does not exist.
Browser highlights this line
userprof = SiteUser.objects.get(id=pk)
This is my views.py:
def userProfile(request, pk):
user = User.objects.get(id=pk)
**userprof = SiteUser.objects.get(id=pk)**
posts = user.post_set.all()
post_comments = user.comment_set.all()
interests = Interest.objects.all()
context = {
'user': user,
'userprof': userprof,
'posts': posts,
'post_comments': post_comments,
'interests': interests
}
return render(request, 'library/profile.html', context)
models.py:
class SiteUser(models.Model):
page_user = models.OneToOneField(User, on_delete=models.CASCADE)
about = HTMLField()
profile_pic = models.ImageField('profile_pic', upload_to='covers', null=True)
Any help would be greatly appreciated.

If you have relations, then always use them if possible. Instead of getting SiteUser having a User pk (it does not have to be always the same), get it using the relation between them:
# bad and risky
user = User.objects.get(id=pk)
userprof = SiteUser.objects.get(id=pk)
# good and always working if object exists
user = User.objects.get(id=pk)
userprof = user.siteuser
Good practice is to name related_name argument for such usage:
class SiteUser(models.Model):
page_user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='foobar')
Then you can call it like:
user = User.objects.get(id=pk)
userprof = user.foobar

User and SiteUser primary keys will not be same. You can easily get SiteUser from the following query:
userprof = user.siteuser # efficient solution
Or
userprof = SiteUser.objects.get(page_user_id=pk) # alternative solution but should not be used in this code sample
Because of their OneToOne relation. For more information, please check the documentation.

Related

How to get object using filter on ManyToManyField

Why target_dialogue is always None?
Model:
class Dialogue(models.Model):
name = models.CharField(max_length=30, blank=True)
is_conference = models.BooleanField(default=False)
participants = models.ManyToManyField(
Person,
related_name='dialogues',
)
def __str__(self):
return self.name or str(self.pk)
And in view I want to get suitable dialogue which contain in participants field 2 objects - user and companion. And if this dialogue doesn't exist I create it:
target_dialogue = None
try:
target_dialogue = Dialogue.objects.get(is_conference=False,participants__in=[user, companion])
except ObjectDoesNotExist:
target_dialogue = Dialogue()
target_dialogue.save()
target_dialogue.participants.add(user)
target_dialogue.participants.add(companion)
finally:
return render(request, 'dialogues/dialogue.html', {
'dialogue': target_dialogue,
})
But target_dialogue is always None. What's a reason of it? I was supposed to solve only a trouble in getting a dialogue from db in order to bad filter parameters, but now I have doubts about it. Maybe something else?
request.user is not a object of Person model with which you have the relation in Dialogue.
You have to first fetch the person object:
user = Person.objecs.get(user=request.user). # According to your person model
Follow same for companion and then query:
target_dialogues = Dialogue.objects.filter(is_conference=False,participants__in=[user,companion]

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.

Updating model objects in the database on Django

I am new to Django and for learning purposes I am trying to build my own site using the linkedn API to display my profile. The following is a a example of my code. To see the whole lot:
https://github.com/javiee/django-site
models.py
class Profile(models.Model):
user = models.ForeignKey(User)
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
def __str__(self):
return self.first_name, self.last_name
class Education(models.Model):
user = models.ForeignKey(User)
school_name = models.CharField(max_length=100)
field_study = models.CharField(max_length=100)
degree = models.CharField(max_length=100)
start_date = models.CharField(max_length=20)
end_date = models.CharField(max_length=20)
and views.py
profile = Profile(first_name = content['firstName'],
last_name = content['lastName'],
user = request.user)
profile.save()
#Education model
content_educ = content['educations']['values']
for value in content_educ:
education = Education(school_name = value['schoolName'],
user = request.user,
field_study = value['fieldOfStudy'],
degree = value['degree'],
start_date = value['startDate']['year'] ,
end_date = value['endDate']['year'])
education.save()
This all working but my problem is that everytime I check linkedn, the code saves all the objects again. What it would ideally do is to "update" fields based on the profile when the .save() method is called. I have read the next link https://docs.djangoproject.com/en/1.7/ref/models/instances/#saving-objects
but I dont manage to get it working, perhaps foreigns keys are not properly set so any advise/help/tip will be much appreciated. Thanks!
Use the update_or_create() method:
Education.objects.update_or_create(
school_name=value['schoolName'],
user = request.user,
defaults={'field_study': value['fieldOfStudy'],
'degree': value['degree'],
'start_date': value['startDate']['year'] ,
'end_date': value['endDate']['year']})
The problem you're having is that you're instantiating new Education instances in these lines:
education = Education(school_name = value['schoolName'],
user = request.user,
field_study = value['fieldOfStudy'],
degree = value['degree'],
start_date = value['startDate']['year'] ,
end_date = value['endDate']['year'])
When Django goes and tries to save these new instances (instances for which id is not yet defined), Django goes ahead and inserts the records rather than doing the update you want.
To do an update, you can either try to get the record, catching the DoesNotExist exception:
try:
education = Education.objects.get(school_name=value['schoolName'],
user=request.user,
field_study=value['fieldOfStudy'],
degree=value['degree'],
start_date=value['startDate']['year'],
end_date=value['endDate']['year'])
except Education.DoesNotExist:
education = Education(school_name=value['schoolName'],
user=request.user,
field_study=value['fieldOfStudy'],
degree=value['degree'],
start_date=value['startDate']['year'],
end_date=value['endDate']['year'])
then apply whatever updates you want/need.
Or you can use get_or_create to do the same:
(education, created) = Education.objects.get_or_create(school_name=value['schoolName'],
user=request.user,
field_study=value['fieldOfStudy'],
degree=value['degree'],
start_date=value['startDate']['year'],
end_date=value['endDate']['year'])
If you don't want to look up your instances by all of those values (they're AND-ed), but want to initialize new instances with certain values, you should look up the defaults keyword for get_or_create.
Or you can use update_or_create as suggested by catavaran.
edit: Or, if you just want to do a straight update of a record without getting it (this also works with multiple objects at once), you can use queryset.update
Education.objects.filter(attribute=value, ...).update(attribute2=value2, ...)

Restrict access to a Class Based View based on the request user and view to be rendered

Here's the scene:
profiles/models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, primary_key = True)
birthdate = models.DateTimeField(blank=True)
def __unicode__(self):
return unicode(self.user)
class SpecialProfile(models.Model):
user = models.OneToOneField(User, primary_key = True)
...
# additional fields here
def __unicode__(self):
return unicode(self.user)
class SpecialProfileURLs(models.Model):
profile = models.OneToOneField(SpecialProfile, primary_key = True)
... #some more URLs
homepage_url = models.URLField(blank = True)
def __unicode__(self):
return unicode(self.profile)
class SpecialProfileImages(models.Model):
profile = models.OneToOneField(SpecialProfile, primary_key = True)
img1 = models.ImageField(blank = True, upload_to='profiles/')
img2 = models.ImageField(blank = True, upload_to='profiles/')
img3 = models.ImageField(blank = True, upload_to='profiles/')
def __unicode__(self):
return unicode(self.profile)`
profiles/views.py
class PublicProfileView(DetailView):
template_name = "public_profile.html"
model = User
class PrivateProfileView(DetailView):
template_name = 'profile/profile2.html'
context_object_name = "profile"
model = User
pk_field = 'pk'
profiles/urls.py
urlpatterns = patterns("",
url(r'^$', 'mysite.views.home', name='home'), # give me nothing? just take me home!
url(r'^(?P<pk>\d+)/$', PrivateProfileView.as_view(), name="profile"),
url(r'^(?P<username>\w+)/$', ProfileRedirectView.as_view(), name="profile_redirect"),
url(r"^edit/(?P<profile_id>\w+)/$", EditProfileView.as_view(), name="profile_update_form"),
)
Here's the problem:
I want to be able to test whether the user giving the request is the same as the ID used to access a profile. I would intercept at a GET request and check, but django gets mad when I do that (probably because it's a DetailView). Is there a recommended/ non-hackish way to be sure only the user to whom this profile belongs can access it? If not, then the user sending the request should be redirected to the PublicProfileView.
It seems bad form to answer my first question but thankfully Alex helped me find exactly what I was looking for:
Per object permissions.
There is a package on GitHub, django-guardian which allows me to set specific permissions to any User or Group for a single model instance.
The documentation is quite thorough.
Your solution is a combination of some different things:
1) Use the PermissionsRequiredMixin from django-braces. This is an easy way to add Permission functionality to class-based views. Documentation on the PermissionsRequiredMixin can be found here.
2) Create your own permission. The Django documentation on that is here. Include this permission in your class based view.
Here's a related answer on SO that addresses the same problem in a function-based view. You can use it to help create your permission.

How to save many to many relationship?

I'm almost using following code(I striped down a little bit), I use auto complete light to load users and users can insert different people name(users) seperated by comma. the problem is when I try to save I get following error
ValueError at /write/
"<Article: test1>" needs to have a value for field "article" before this many-to-many relationship can be used.
models.py
class Article(models.Model):
author = models.ForeignKey(User,)
people = models.ManyToManyField(User, related_name="with", null=True,)
content = models.TextField()
forms.py
class ArticleForm(forms.ModelForm):
people = forms.CharField(widget=autocomplete_light.TextWidget('UserAutocomplete'))
class Meta:
model = Article
views.py
def write(request):
if request.POST:
form = ArticleForm(request.POST)
if form.is_valid():
content = form.cleaned_data['content']
user = User.objects.get(username=request.user.username)
people_str = form.cleaned_data['accompanied']
people = [x.strip() for x in accompanied_str.split(',')]
article = Article(
content = content,
author = user,
)
for username in accompanied:
user = User.objects.get(username=username)
article.people.add(user)
article.save()
return HttpResponseRedirect('/success/')
In order to create a relation you need the ids of both side. The newly created article has no id yet. If you save the article first and then add people to it it should work fine.
article = Article(
content = content,
author = user,
)
article.save()
article.add(*User.objects.filter(username__in=accompanied))
The process of adding people can be cheaper by getting all users that have a username from the list of accompanied in one fetch.