I had two fields available_to and available_from in a model called Hotel. When creating a new hotel, we got to specify the date and time! I want to validate these two fields, in such a way that, if one selects available_to as 23/2/2015 , the available_from should not be 23/2/2014.
The fastest way is creating a custom validation method within model itself.
Try updating Hotel as follows:
class Hotel < ActiveRecord::Base
validate :validate_available_to if Proc.new { |h| h.available_from && h.available_to }
def validate_available_to
return if available_to > available_from
errors.add(:available_to, "Date should be at least #{available_from.next.strftime("%d/%m/%Y")}")
end
end
How to use
h = Hotel.new
h.available_from = Date.parse("22/2/2014")
h.available_to = Date.parse("22/2/2014")
h.valid?
# => false
h.errors[:available_to]
# => ["Date should be at least 23/02/2014"]
However, when you're extending you validation rules, and code gets complicated, you should consider moving away validation from Model to separate Validation classes.
Check validates_with, with ActiveModel::Validator for more.
Hope that helps!
Related
I'm trying to dynamically include/exclude particular fields on my ModelSerializer depending on the instance itself. So, assume I have a hierarchical model which represents geography using self-joins:
class TreeModel():
name = CharField()
kind = CharField(choices=['country', 'state', 'city'])
parent = ForeignKey(self, related_name='children')
Given that, say I wanted to hide the 'children' relationship links of an instance when the kind is 'state' but then show it when the kind was 'country'. I tried fiddling with get_fields method but that didn't work.
I'm looking to do this because in my model some instances of the TreeModel class have thousands of children, but others have only a few. I don't want to show the children for certain instance types because it is killing performance and I only need them for a subset. Thnaks
This is what you're looking for.
DRF allows you to dynamically modify fields at the time of initialization of the serializer.
class TreeSerializer:
def __init__(self , instance , *args , **kwargs ):
super().__init__(instance , *args , **kwargs)
if instance.kind == 'state':
self.fields.pop('children')
#Other Conditions
This example would hold because the first positional argument to a serializer is always the model instance.
There's another way to doing this without modifying the serializer : Using Django signals.
Here's my answer demonstrating how to use them
class TreeSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
if instance.kind == 'state':
self.fields.pop('children')
return super().to_representation(instance)
Hi I have a model called PurchasingGroup, a purchasing group has many Goals.
Goal model has 2 attributes: no_of_users and discount
I need to validate that the goals are consecutive, for example if I create a Goal with no_of_users = 10 and discount = 15 then the next goal I create must have greater values, otherwise I have to show the error to the user, right now Im making the validation in the create action of the controller, I know it is a bad practice so I want to know how to create this validation, I could not achieved it using custom validations in the model level.
I need to access the purchasing group and then check if the last group goal values are greater than or equal to the values of the new goal:
Below is the validation I have in the controller, it works but I want to do it right:
def create
respond_to do |format|
#purchasing_group = PurchasingGroup.find params[:purchasing_group_id]
#goal = Goal.new goal_params
#error_messages = ""
if not #purchasing_group.goals.empty?
if #purchasing_group.goals.last.no_of_users >= #goal.no_of_users
#error_messages = "The goals are consecutive! No. Users: must be greater than the previous goal value"
end
if #purchasing_group.goals.last.discount >= #goal.discount
#error_messages = "#{#error_messages}\nThe goals are consecutive! discount: must be greater than the previous goal value"
end
end
#if there are no errors then we save the object
if #error_messages.empty?
if #goal.save
#goal.update_attributes purchasing_group_id: params[:purchasing_group_id]
end
end
#In a js template I handle the errors, that is not relevant for this question.
format.js
end
end
if I understood you right, then:
validate :count_no_of_users
private
def count_no_of_users
last_goal = PurchasingGroup.find(self.purchasing_group_id).goals.last
error(:count_no_of_user, "Should be more than #{last_goal.no_of_user}") if self.no_of_user < last_goal.no_of_user
end
and same for discount
you can validate it in single or separate validations.
Is it better to use nested relationships or PrimaryKeyRelated field if you have lots of data?
I have a model with deep relationships.
For simplicity I did not add the colums.
Model:
Usecase:
User creates 1 Workoutplan with 2 Workouts and 3 WorkoutExercises.
User creates 6 Sets for each WorkoutExercise/Exercise.
User starts workout > new FinishedWorkout is created
User does first exercise and enters the used weights > new FinishedWorkoutExercise with FinishedSet is created
Question:
I want to track the progression for each workoutplan > workout > exercise.
So with time the user may have finished dozens of workouts therefore hundreds if sets are already in the database.
If I now use nested Relationships I may load a lot of data I don't need.
But if I use PrimaryKeyRelatedFields I have to load all the data I need separately which means more effort in my frontend.
Which method is preferred in such a situation?
Edit:
If I use PrimaryKeyRelatedFields how do I distinguish if e.g. Workouts in Workoutplan is an array with primary keys or an array with the loaded objects?
If you use PrimaryKeyRelatedField, you'll have a big overload to request the the necessary data in frontend
In your case, I would create specific serializers with the fields you want (using Meta.fields attribute). So, you won't load unecessary data and the frontend won't need to request more data from backend.
I can write a sample code, if you need more details.
I'll get to the question regarding serializers in a second, but first of all and for clarification. What is the purpose of having duplicate models as Workout/Finished Workout, Set/Finished Set,...?
Why not...
class Workout(models.Model):
#...stuff...
finished = models.DateTimeField(null=True, blank=True)
#...more stuff...
Then you can just set a finished date on a workout when it's done.
Now, regarding the question. I would suggest you think about user interactions. What parts of the front-end are you trying to populate? How is the data related and how would the user access it?
You should think about what parameters you're querying DRF with. You can send a date and expect workouts finished on a specific day:
// This example is done in Angular, but you get the point...
var date= {
'day':'24',
'month':'10',
'year':'2015'
};
API.finishedWorkout.query(date).$promise
.then(function(workouts){
//...workouts is an array of workout objects...
});
Viewset...
class FinishedWorkoutViewset(viewsets.GenericAPIView,mixins.ListModelMixin):
serializer_class = FinishedWorkOutSerializer
queryset = Workout.objects.all()
def list(self, request):
user = self.request.user
day = self.data['day'];
month = self.data['month'];
year = self.data['year'];
queryset = self.filter_queryset(self.get_queryset().filter(finished__date=datetime.date(year,month,day)).filter(user=user))
page = self.paginate_queryset(queryset)
serializer = self.get_serializer(queryset, many=True)
return response.Response(serializer.data)
And then your FinishedWorkoutSerializer can just have whatever fields you want for that specific type of query.
This leaves you with a bunch of very specific URLs, which isn't all that great, but you can use specific serializers for those interactions and you're also open to dynamically changing the filter, depending on what paramaters are in self.data.
There is also a chance that you may want to filter differently depending what method is being called, say you want to list only active exercises, but if a user queries a specific exercise, you want him to have access to it (note that the Exercise object should have a models.BooleanField attribute called "active").
class ExerciseViewset(viewsets.GenericViewSet, mixins.RetrieveModelMixin, mixins.ListModelMixin):
serializer_class = ExerciseSerializer
queryset = Exercise.objects.all()
def list(self, request):
queryset = self.filter_queryset(self.get_queryset().filter(active=True))
page = self.paginate_queryset(queryset)
serializer = self.get_serializer(queryset, many=True)
return response.Response(serializer.data)
Now you have different objects show up on the same URL, depending on the action. It's a bit closer to what you need, but you're still using the same serializer, so if you need a huge nested object on retrieve(), you're also gonna get a bunch of them when you list().
In order to keep lists short and details nested, you need to use different serializers.
Let's say you want to only send exercises' pk and name attributes when they are listed, but whenever an exercise is queried, you wan't to send along all related "Set" objects ordered inside an array of "WorkoutSets"...
# Taken from an SO answer on an old question...
class MultiSerializerViewSet(viewsets.GenericViewSet):
serializers = {
'default': None,
}
def get_serializer_class(self):
return self.serializers.get(self.action, self.serializers['default'])
class ExerciseViewset(MultiSerializerViewSet, mixins.RetrieveModelMixin, mixins.ListModelMixin):
queryset = Exercise.objects.all()
serializers = {
'default': SimpleExerciseSerializer,
'retrieve': DetailedExerciseSerializer
}
Then your serializers.py could look a bit like...
#------------------Exercise
#--------------------------Simple List
class SimpleExerciseSerializer(serializers.ModelSerializer):
class Meta:
model Exercise
fields = ('pk','name')
#--------------------------Detailed Retrieve
class ExerciseWorkoutExerciseSetSerializer(serializers.ModelSerializer):
class Meta:
model Set
fields = ('pk','name','description')
class ExerciseWorkoutExerciseSerializer(serializers.ModelSerializer):
set_set = ExerciseWorkoutExerciseSetSerializer(many=True)
class Meta:
model WorkoutExercise
fields = ('pk','set_set')
class DetailedExerciseSerializer(serializers.ModelSerializer):
workoutExercise_set = exerciseWorkoutExerciseSerializer(many=True)
class Meta:
model Exercise
fields = ('pk','name','workoutExercise_set')
I'm just throwing around use cases and attributes that probably make no sense in your model, but I hope this is helpfull.
P.S.; Check out how Java I got in the end there :p "ExcerciseServiceExcersiceBeanWorkoutFactoryFactoryFactory"
Say I have the following in my controller:
#category1
#category2
and I want to find all stores associated with those two categories...
#stores = #category1.stores + #category2.stores
this does work, but unfortunately returns an unaltered Array, rather than a AR::Base Array, and as such, I can't do things like pagination, scope, etc...
It seems to me like there's a built-in way of finding through multiple instance association... isn't there?
##stores = #category1.stores + #category2.stores
#if you want to call API methods you can just add conditions with the category id
#stores = Store.find(:all, :conditions => ['category_id=?', a || b])
With ActiveRecord, whenever you're finding a set of unique model objects, calling find on that model is usually your best bet.
Then all you need to do is constrain the join table with the categories you care about.
#stores = Store.all(:joins => :categories,
:conditions => ['category_stores.category_id in (?)', [#category1.id, #category2.id]])
I have to implemene Single Table Inheritance for a class Person who can be of type Teacher, Student,Outsider.
class Person < ActiveRecord::Base
end
class Teacher < Person
end
class Student < Person
end
class Outsider < Person
end
What changes do I need to make in the routes and the forms while register a new user. I have a column(string) "type" in the people table which can be implemented as a dropdown in the form to register a new user. Is there anything else that I need to do in the form so the user is registered as a particular type of Person? Do I need to make any changes in the routes too?
Since you use one form to create all types of Persons, then you should stick with one Controller as well so you don't need to add any additional routes.
The type attribute is not really something you should assign manually to an instance, it should be set automatically by choosing which type of model to create.
I don't know how it looks in your controller and views, but you can extract the type of model to create like this:
class_type = params[:type].constantize
#person = class_type.new
On the other hand, if the type attribute is nested in a form_for in your view, then the type attribute is probably send to the controller like params[:person][:type] in which case it should be removed from the :person hash before it is used to create the new instance. Perhaps something like this:
class_type = params[:person].delete(:type).constantize
#person = class_type.new(params[:person])
Except adding a dropdown list of type selection in the form, there's nothing more to do. You can create a user in the normal way, like:
#user = Person.new params[:user]
But the type attribute could not be mass assigned, so you have to assign it separately.
#user.type = sanitize_user_type params[:user][:type]
The method sanitize_user_type is used to validate user input value.
The route for creating new user doesn't need to change. Whether other routes need to change or not depend on your requirement. Actually you can add the routes for Teacher, Student, Outsider and relative controllers, so that you can build restful urls.