I have a users and venues model. venues belongs_to users and users can have_many venues.
I would like a user to be able to set a default venue. I am new to programming/development and am keen to learn best practices.
So, should a venue have a boolean 'default_venue' column which only allows one row to be 'true' per user. Or should I be creating a default_venue model? And if so, any tips on what/how this would look like?
I would just add a default_venue_id to the Users table. Then write a small method on the User model.
# User.rb
...blah blah validations
...blah blah other code
def default_venue
Venue.where(id: default_venue_id)
end
Then going forward in your Controllers (or Rails Console), this would be valid code:
#Returns default venue for User
User.find(:id).default_venue
Related
I have to make a system which will allow users to progressively fill out surveys based on whether or not the initial survey was accepted.
What this means is that after the user has logged in, they need to be able to fill in a preliminary survey for screening purposes. Only once this preliminary screening survey has been approved by an administrative user (superuser) will the user have access to the second (and final) survey.
I have a survey model which looks like this:
class Screening_survey(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=20)
surname = models.Charfield(max_length=20)
salary = models.IntegerField()
is_accepted = models.NullBooleanField()
When the user posts this initial survey, they only fill in the name, surname and salary fields. When the survey is initially posted, 'user' is set to the current logged in user (request.user) and is_accepted is simply set to 'unknown' automatically in the background. It is then up to the administrative superuser to change the is_accepted value to 'yes' or 'no'.
If the value of the is_accepted field is set to yes for a particular user's form, then that user should be able to access the next survey.
I have looked at Django permissions, but I do not think the default permissions will work for me seeing as I don't want users of a specific /group/ to have access to specific pages; rather I want users whose initial surveys have been approved to have access to the next view (the final survey).
I have also given the #user_passes_test decorator some thought, but again it seems as if it checks permissions only on a user basis and not on the basis of a user and a model that is linked to them.
I know this question seems simple -- I'm quite new to Django as well as programming in general).
Thank you for your help.
You're spot on - you will need to define your own logic to see if the user has been approved, not permissions. Permissions don't really allow you to test on a per-user or per-model basis the way you're looking for.
There are two places to define where this test will be. Either in the view itself, or via your own #user_passes_test decorator. You already have a Foreign Key to the user object, so you can write a test in the view, like this:
#login_required
def survey_view(request):
survey = Screening_survey.objects.filter(user=request.user)
if survey and survey.is_accepted is 'yes':
# Do the remaining view logic
else:
raise PermissionDenied
If you want to put this logic in the #user_passes_test, that would work well too. The user object is passed directly (see the docs for an example)
Each Widget has an associated User. Users have a String attribute called email which stores the email address associated with the User.
The User model has a unique id attribute, as per Rails conventions, but also ensures that email addresses are unique. Therefore, User.where(email: foo#example.com) is guaranteed to return at most one result.
In a form to create a Widget, I would like users to be able to specify an email address, rather than an ID. Both id and email are unique keys for Users, but email addresses are more convenient for people.
What's the recommended way for the Widget controller to accept an association reference that names the associate by a key other than id? When using id, if the widget[user_id] parameter stores the ID for a User, mass-assignment (in create or update) stores the correct User in the Widget. Is there any built-in Rails support for making something like widget[user_email] work, or should I add code in the controller to recognize that parameter, look up the User, and associate the User with the Widget?
Here's the code:
class Widget < ActiveRecord::Base
has_one :user
end
class User < ActiveRecord::Base
end
class WidgetsController
load_and_authorize_resource
def create
#widget.save
end
def widget_params
params.require(:widget).permit(...)
end
end
I've considered:
Modifying the Widget model to add a setter for "user_email", looking up the User there, and setting "user_id" appropriately.
Modifying WidgetsController#widget_params to notice the presence of "widget[user_email]", look up the User, and replace it with "widget[user_id]".
I'm leaning towards the second option.
Is there a better way?
I am working in django, am planning a database for rides for users.
each User can be on multiple Rides (over time) and each Ride can have multiple Users (passengers) in it.
Also, for each Ride there has to be only one Driver (also a User) so I think I have a many-to many relationship between the Rides and Users tables for what user is on what ride, and also a One-To-Many relationship between the Rides's Driver_id and the User_id. right?
My questions are-
I saw in the django docs that I should put a many-to-many field in One of the models. Does it matter which one? and also, does it create a new table like rides_users?
and also, what is the difference (in One-To-many relationship) between using a foreignKey field and a OneToManyField field?
EDIT:
Currently, there are my models:
def get_image_path(models.Model):
return os.path.join('photos',str(instance.id),filename)
class UserProfile(models.Model):
user=models.OneToOneField(User)
phone_number=models.CharField(max_length=12)
profile_picture=models.ImageField(upload_to=get_image_path, black=True, null=True)
class Ride(models.Model):
driver=models.ForeignKey(UserProfile, related_name="r_driver")
destination=models.ForeignKey(Destination, related_name="r_final_destination")
leaving_time=models.DateTimeField()
num_of_spots=models.IntergerField()
passengers=models.ManyToMany(UserProfile, related_name="r_passengers")
mid_destinations=models.ManyToMany(Destination, related_name="r_mid_destinations")
class Destination(models.Model):
name=models.CharField(max_length=30)
As you can see, each Ride has multiple mid_destination and multiple passengers. a Ride also has One driver and One final destination.
The Issue is - when a User adds a Ride, I want the driver, destination and mid_destinations and the rest of the fields to be set by the User (the driver is user adding the Ride), Except for the passengers field. I want the other Users to add themselves to the ride, so when the Ride is created the User (driver) doesn't have to set the passengers.
How do I go about it? and also, any other suggestions about the models?
There is no such thing as a OneToManyField.
It doesn't matter from a practical point of view which side the ManyToManyField lives on. Personally, I'd put it on Ride, both to avoid changing the User model and because conceptually I'd say that rides are the main objects here.
And yes, adding the field will automatically create the linking table.
what you want is probably something like this
class MyModel(models.Model):
driver = models.ForeignKey(to=User, related_name='r_driver')
# you need to use a related name if you want to link to the same model more than once
passengers = models.ManyToManyField(User, related_name="r_passengers")
Sorry, I am still new to Django, hopefully the question isn't out of place.
When I have the following in my template:
<td>{{ item.last_name }}</td>
By clicking on last name the user will be redirected to the following link in order to edit it.
http://127.0.0.1:8000/contact/edit/?id=1
But then what prevents any logged in user to just inject a different id in there on the browser and edit a record that doesn't belong to him?
Update
I just had an idea when I read the comment and answer below. Rather than using a third party app, couldn't I just create a UserProfile for each user and attach a unique company wide uuid.uuid1(). Each time a loggedin user attempts to edit something, his unique company uuid will be also passed in the link as an additional parameter.
On the edit side, it would harvest this guid and compare it against the logged in user and see if they match. If they do match, he is authorized to proceed with the editing, otherwise he will be redirected.
What do you think? Any weaknesses?
If you use Django's new class based views, e.g. the generic UpdateView, you can extend the dispatch handler.
def dispatch(self, request, *args, **kwargs):
handler = super(MyEditView, self).dispatch(request, *args, **kwargs)
# Only allow editing if current user is owner
if self.object.author != request.user:
return HttpResponseForbidden(u"Can't touch this.")
return handler
In this case, the code verifies that the author field of the model object corresponds to the currently logged in user, before even handling the rest of the request.
You can see a reallife example of this in a project of mine.
When you're using Django auth, always rely on the session mechanism to identify an user instead of making some other id such as uuid1() (except, for example, when you need extra sessions in an e-commerce site under HTTPS).
For the permission part, you could check the ownership directly, mainly as described by Koliber Services. The relationships between Company, User and Contact are crucial for the logic of permission checking. There are many ways to model the relationships and thus the code would differ much. For example:
# a modeling way
User.company -> Company : an user belongs to a company
Contact.contributor -> User : a contact is contributed by an user, would be invalid is user is leaving the company
# could check accessibility by
can_view = contact.contributor.company_id == current_user.company_id
# another modeling way
User.company -> Company : an user belongs to a company
Contact.company -> Company : a contact info is owned by a company, does not share globally
# could check accessibility by
can_view = contact.company_id == current_user.company_id
When can_view is False, user should get a 403 for his unauthorized attempting and get logged.
Normally the above method is enough for content protection(not yet in Django Admin). However, when you have many different types of permission checking and even row-permission checkings, it's better to use some uniform permission API.
Take Django-guardian for example, you could simply map companies to groups and assign can_view permission for a contact for the group representing the user's company. Or, assign the can_view permission to all users in a company when a contact is created by using signal or celery task.
Furthermore, you could use /contact/1/edit/ instead of /contact/edit/?id=1. In this way the int(request.GET('id')) part is moved to urlconf like r'^contact/(?P<pk>\d+)/$', less code and much clearer.
There are some third-party apps that does what you want, its called "row-level permission" where you can give different users different access to specific objects, "Row level" comes from SQL where each object is a row in the database
I use django-guardian to do the job
In the function handling the saving of the data, check to see if the object being edited has the same ID as the presently logged in user.
For example, if the object in question is called EmailPrefs and it has a field called user_id:
Load the EmailPrefs object with the ID of the object being edited
If the user_id does not match the current user, stop further processing
Modify the EmailPrefs object
Save the EmailPrefs object to the database
I have some interrelated models that need to co-exist on a single admin page. Here's the idea:
Theater productions have cast members, and cast members have specified roles. A theater production is related to a given written text (play, adaptation, etc.), and the written text holds a list of all the roles for that text. When adding a Production, each cast member needs to be associated with one of those roles.
Here's how the data model is working:
Models: Production, Person, CastMember, Role, WrittenText
Relationships: Production and Person have an M2M relationship through CastMember, which adds a "role" field - a ForeignKey to a Role object. Role itself has a ForeignKey to a WrittenText object.
So, the problem is this: in the admin page for Productions, I have a TabularInline to add CastMembers. The CastMember entries in the table should have their 'role' field limited to only the roles specified in the WrittenText that the Production references.
I made a half-way solution to the problem by overriding the model form:
class CastMemberForm(ModelForm):
class Meta:
model = CastMember
def __init__(self, *args, **kwargs):
super(CastMemberForm, self).__init__(*args, **kwargs)
if 'instance' in kwargs:
self.fields['role'].queryset = Role.objects.filter(source_text=self.instance.production.source_text)
But, this only works if you choose a Person from the drop-down, save, and then choose the role - otherwise you just get a list of all roles. Taking out "if 'instance' in kwargs" gives me a DoesNotExistError.
Is this just way too complex to do without something like client-side JS, or is there a simpler solution that I'm missing?
Here is an example of chained select boxes via javascript/ajax. It should basically be the same principle, but you should need to tweak the js to not update one select box, but all of them in the inline admin... Maybe this gives you a small inspiration!