জ্যাঙ্গো ফর্মসেটে কাস্টম ফর্ম প্যারামিটারগুলি পাস করছে


150

এটি form_kwargs সহ জ্যাঙ্গো ১.৯ এ স্থির করা হয়েছিল ।

আমার কাছে একটি জাঙ্গো ফর্ম রয়েছে যা দেখতে এটির মতো দেখাচ্ছে:

class ServiceForm(forms.Form):
    option = forms.ModelChoiceField(queryset=ServiceOption.objects.none())
    rate = forms.DecimalField(widget=custom_widgets.SmallField())
    units = forms.IntegerField(min_value=1, widget=custom_widgets.SmallField())

    def __init__(self, *args, **kwargs):
        affiliate = kwargs.pop('affiliate')
        super(ServiceForm, self).__init__(*args, **kwargs)
        self.fields["option"].queryset = ServiceOption.objects.filter(affiliate=affiliate)

আমি এই ফর্মটি এই জাতীয় কিছু দিয়ে ডাকি:

form = ServiceForm(affiliate=request.affiliate)

request.affiliateলগ ইন ব্যবহারকারী যেখানে । এটি উদ্দেশ্য হিসাবে কাজ করে।

আমার সমস্যা হ'ল আমি এখন এই একক ফর্মটিকে ফর্মসেটে রূপান্তর করতে চাই। আমি কী বুঝতে পারি না যে ফর্মसेटটি তৈরি করার সময় আমি কীভাবে অনুমোদিত তথ্য পৃথক ফর্মগুলিতে পাস করতে পারি। এর বাইরে একটি ফর্মসেট তৈরি করতে ডক্স অনুসারে আমাকে এরকম কিছু করা দরকার:

ServiceFormSet = forms.formsets.formset_factory(ServiceForm, extra=3)

এবং তারপরে আমার এটি তৈরি করা দরকার:

formset = ServiceFormSet()

এখন আমি কীভাবে এফিলিয়েট = অনুরোধটি পাস করতে পারি? এইভাবে স্বতন্ত্র ফর্মগুলিতে অনুমোদিত?

উত্তর:


105

আমি functools.partial এবং functools.wraps ব্যবহার করব :

from functools import partial, wraps
from django.forms.formsets import formset_factory

ServiceFormSet = formset_factory(wraps(ServiceForm)(partial(ServiceForm, affiliate=request.affiliate)), extra=3)

আমি মনে করি এটিই সবচেয়ে পরিষ্কার পদ্ধতির, এবং কোনওভাবেই সার্ভিসফর্মকে প্রভাবিত করে না (অর্থাত্ সাবক্লাস করা কঠিন করে)।


এটা আমার পক্ষে কাজ করছে না আমি ত্রুটিটি পেয়েছি: অ্যাট্রিবিউটআরার: '_curriedFormSet' অবজেক্টের 'get'
পাওলো বার্গান্টিনো

আমি এই ত্রুটিটির সদৃশ করতে পারি না। এটিও একটি অদ্ভুত কারণ একটি ফর্মসেটে সাধারণত 'গেট' বৈশিষ্ট্য থাকে না, সুতরাং মনে হয় আপনি আপনার কোডটিতে কিছু অদ্ভুত কাজ করছেন। (এছাড়াও, '_curriedFormSet' এর মতো বিজোড়তা থেকে মুক্তি পাওয়ার জন্য আমি উত্তরটি আপডেট করেছি)।
কার্ল মায়ার

আমি এটি পুনর্বিবেচনা করছি কারণ আমি আপনার সমাধানটি কাজ করতে চাই। আমি ফর্মसेटটি সূক্ষ্মভাবে ঘোষণা করতে পারি, তবে আমি যদি প্রিন্ট করার চেষ্টা করি I {formset} doing এমন হয় যখন আমি পাই "তাত্পর্য 'পেতে" ত্রুটি পাওয়া যায়। এটি আপনার সরবরাহিত সমাধানগুলির সাথেই ঘটে। আমি যদি ফর্মसेटটি লুপ করে ফর্মগুলি {{ফর্ম} as হিসাবে মুদ্রণ করি তবে আমি আবার ত্রুটি পেয়েছি। যদি আমি উদাহরণস্বরূপ lo {form.as_table} as হিসাবে লুপ করে মুদ্রণ করি তবে আমি খালি ফর্ম টেবিলগুলি পেয়েছি, যেমন। কোন ক্ষেত্র মুদ্রিত হয়। কোন ধারনা?
পাওলো বার্গান্টিনো

আপনি ঠিক বলেছেন, আমি দুঃখিত; আমার আগের পরীক্ষা খুব বেশি পর্যায়ে যায় নি। আমি এটিকে সন্ধান করেছি এবং ফর্মসেটগুলি অভ্যন্তরীণভাবে কাজ করার পথে কিছু অদ্ভুততার কারণে এটি ভেঙে গেছে। সমস্যাটি ঘিরে কাজ করার একটি উপায় রয়েছে, তবে এটি মূল কমনীয়তা হারাতে শুরু করে ...
কার্ল মেয়ার

5
যদি এখানে মন্তব্য থ্রেডটি বোঝায় না, এটি কারণ আমি functools.partialজাজানো পরিবর্তে পাইথনের ব্যবহারের উত্তরটি সম্পাদনা করেছি django.utils.functional.curry। তারা একই কাজটি ব্যতীত, functools.partialনিয়মিত পাইথন ফাংশনের পরিবর্তে একটি পৃথক কলযোগ্য প্রকারটি প্রদান করে এবং partialপ্রকারটি উদাহরণ পদ্ধতি হিসাবে আবদ্ধ হয় না, যা এই মন্তব্য থ্রেডটি মূলত ডিবাগিংয়ের জন্য উত্সর্গীকৃত সমস্যার সমাধান করে ne
কার্ল মায়ার

81

অফিসিয়াল ডকুমেন্ট ওয়ে

জ্যাঙ্গো ২.০:

ArticleFormSet = formset_factory(MyArticleForm)
formset = ArticleFormSet(form_kwargs={'user': request.user})

https://docs.djangoproject.com/en/2.0/topics/forms/formsets/#passing-custom-parameters-to-formset-forms


8
এটি এখন এটি করার সঠিক উপায় হওয়া উচিত। গৃহীত উত্তরটি কাজ করে এবং দুর্দান্ত তবে হ্যাক
জুনচাও গু

এটি করার জন্য অবশ্যই সেরা উত্তর এবং সঠিক উপায়।
yaniv14

এছাড়াও জ্যাঙ্গো 1.11 কাজ করে docs.djangoproject.com/en/1.11/topics/forms/formsets/...
ruohola

46

আমি একটি ফাংশনে গতিশীলভাবে ফর্ম শ্রেণিটি তৈরি করব, যাতে এটি বন্ধের মাধ্যমে অনুমোদিত হতে অ্যাক্সেস পেতে পারে:

def make_service_form(affiliate):
    class ServiceForm(forms.Form):
        option = forms.ModelChoiceField(
                queryset=ServiceOption.objects.filter(affiliate=affiliate))
        rate = forms.DecimalField(widget=custom_widgets.SmallField())
        units = forms.IntegerField(min_value=1, 
                widget=custom_widgets.SmallField())
    return ServiceForm

বোনাস হিসাবে, আপনাকে বিকল্প ক্ষেত্রে ক্যোরিসেটটি পুনরায় লিখতে হবে না। অবক্ষয়টি হ'ল সাবক্লাসিংটি একটু মজাদার। (যে কোনও সাবক্লাস একই পদ্ধতিতে তৈরি করতে হবে))

সম্পাদনা:

একটি মন্তব্যের জবাবে আপনি ক্লাসের নামটি যে কোনও জায়গাতে ব্যবহার করবেন এমন জায়গা সম্পর্কে আপনি এই ফাংশনটিতে কল করতে পারেন:

def view(request):
    affiliate = get_object_or_404(id=request.GET.get('id'))
    formset_cls = formset_factory(make_service_form(affiliate))
    formset = formset_cls(request.POST)
    ...

ধন্যবাদ - যে কাজ করেছে। আমি এটি স্বীকৃত হিসাবে চিহ্নিত করে ধরে রাখছি কারণ আমি এক ধরণের আশা করছি যে কোনও ক্লিনার বিকল্প রয়েছে কারণ এটি এভাবে করা নিশ্চিতভাবে মজাদার মনে হয়।
পাওলো বার্গান্টিনো

আপাতদৃষ্টিতে এটি করার সর্বোত্তম উপায় হিসাবে গ্রহণযোগ্য হিসাবে চিহ্নিত করা। অদ্ভুত লাগছে, তবে কৌশলটি করে। :) ধন্যবাদ.
পাওলো বার্গান্টিনো

কার্ল মেয়ারের রয়েছে, আমি মনে করি, আপনি যে ক্লিনারটি সন্ধান করেছিলেন।
জারেট হার্ডি

আমি জ্যাঙ্গো মডেলফর্মগুলির সাথে এই পদ্ধতিটি ব্যবহার করছি।
শেফসমার্ট

আমি এই সমাধানটি পছন্দ করি, তবে কোনও ফর্মসেটের মতো দৃশ্যে এটি কীভাবে ব্যবহার করবেন তা নিশ্চিত নই। কীভাবে কোন দৃষ্টিতে এটি ব্যবহার করবেন তার কোনও ভাল উদাহরণ আছে? কোন পরামর্শ প্রশংসা করা হয়।
জো জে

16

এটিই আমার পক্ষে কাজ করেছে, জাঙ্গো ১. 1.:

from django.utils.functional import curry    

lols = {'lols':'lols'}
formset = modelformset_factory(MyModel, form=myForm, extra=0)
formset.form = staticmethod(curry(MyForm, lols=lols))
return formset

#form.py
class MyForm(forms.ModelForm):

    def __init__(self, lols, *args, **kwargs):

আশা করি এটি কাউকে সাহায্য করবে, এটি বের করার জন্য আমাকে যথেষ্ট সময় নিয়েছে;)


1
আপনি আমাকে staticmethodএখানে ব্যাখ্যা প্রয়োজন কেন ?
fpghost

9

আমি "ক্লিনার" এবং আরও পাইথোনিক হওয়ার জন্য ক্লোজার সলিউশনটি পছন্দ করি (সুতরাং +1 টি ম্যামশাল উত্তর) তবে জ্যাঙ্গো ফর্মগুলির কাছে একটি কলব্যাক প্রক্রিয়াও রয়েছে যা আপনি ফর্মেটগুলিতে ক্যোয়ারসেটগুলি ফিল্টার করার জন্য ব্যবহার করতে পারেন।

এটি নথিভুক্তও নয়, যা আমি মনে করি এমন একটি সূচক যা জ্যাঙ্গো ডেভস সম্ভবত এটি পছন্দ করবে না।

সুতরাং আপনি মূলত আপনার ফর্মসেটটি একই তৈরি করেছেন তবে কলব্যাক যুক্ত করুন:

ServiceFormSet = forms.formsets.formset_factory(
    ServiceForm, extra=3, formfield_callback=Callback('option', affiliate).cb)

এটি এমন শ্রেণীর উদাহরণ তৈরি করছে যা দেখে মনে হচ্ছে:

class Callback(object):
    def __init__(self, field_name, aff):
        self._field_name = field_name
        self._aff = aff
    def cb(self, field, **kwargs):
        nf = field.formfield(**kwargs)
        if field.name == self._field_name:  # this is 'options' field
            nf.queryset = ServiceOption.objects.filter(affiliate=self._aff)
        return nf

এটি আপনাকে সাধারণ ধারণা দেয় should এটি কলব্যাকটিকে একটি অবজেক্ট পদ্ধতি হিসাবে তৈরি করা আরও জটিল, তবে একটি সাধারণ ফাংশন কলব্যাক করার বিপরীতে আপনাকে আরও কিছুটা নমনীয়তা দেয়।


1
উত্তরের জন্য ধন্যবাদ. আমি এখনই মিমারশালের সমাধানটি ব্যবহার করছি এবং যেহেতু আপনি সম্মত হন এটি আরও পাইথোনিক (এটি আমার প্রথম পাইথন প্রকল্প হিসাবে আমি জানতে পারি না) আমি অনুমান করি যে আমি এটির সাথে আঁকছি। যদিও কলব্যাক সম্পর্কে জেনে রাখা ভাল অবশ্যই। আবার ধন্যবাদ.
পাওলো বার্গান্টিনো

1
ধন্যবাদ. এই পদ্ধতিটি মডেলফর্মসেট_ফ্যাক্টরির সাথে দুর্দান্ত কাজ করে। আমি মডেলফরমেটসের সাথে সঠিকভাবে কাজ করার অন্যান্য উপায়গুলি পেতে পারি না তবে এই পথটি খুব সোজা ছিল।
স্পাইপ

কারি ফাংশনাল মূলত একটি ক্লোজার তৈরি করে, তাই না? আপনি কেন বলেছেন যে @ এমআরশালের সমাধানটি বেশি পাইথোনিক? বিটিডব্লিউ, আপনার উত্তরের জন্য ধন্যবাদ। আমি এই পদ্ধতির পছন্দ।
জোশ

9

আমি কার্ল মিয়ার্সের উত্তরের মন্তব্য হিসাবে এটি রাখতে চেয়েছিলাম, তবে যেহেতু এর জন্য পয়েন্ট দরকার আমি এটি এখানে রেখেছি। এটি নির্ধারণ করতে আমার 2 ঘন্টা সময় নিয়েছে তাই আমি আশা করি এটি কারও সাহায্য করবে।

ইনলাইনফর্মसेट_ফ্যাক্টরি ব্যবহার সম্পর্কে একটি নোট।

আমি সেই সমাধানটি আমার স্ব ব্যবহার করেছি এবং এটি ইনলাইনফরমেট_ফ্যাক্টরির সাথে পরীক্ষা না করা অবধি নিখুঁতভাবে কাজ করে। আমি জ্যাঙ্গো ১.০.২ চালাচ্ছি এবং কিছু অদ্ভুত কী-এরর ব্যতিক্রম পেয়েছি। আমি সর্বশেষ ট্রাঙ্কে আপগ্রেড করেছি এবং এটি সরাসরি কাজ করেছে।

আমি এখন এটি এর অনুরূপ ব্যবহার করতে পারি:

BookFormSet = inlineformset_factory(Author, Book, form=BookForm)
BookFormSet.form = staticmethod(curry(BookForm, user=request.user))

একই জিনিস জন্য যায় modelformset_factory। এই উত্তরের জন্য ধন্যবাদ!
thnee

9

প্রতিশ্রুতি হিসাবে e091c18f50266097f648efc7cac2503968e9d217 মঙ্গলবার আগস্ট 14 23:44:46 2012 +0200 এ গৃহীত সমাধানটি আর কাজ করতে পারে না।

ডিজেঙ্গো.ফর্মস.মোডেলস.মোডেলফর্ম_ফ্যাক্টরি () ফাংশনের বর্তমান সংস্করণটি "টাইপ নির্মাণ কৌশল" ব্যবহার করে, মেটাক্লাসের ধরণের জন্য পাস ফর্মটিতে টাইপ () ফাংশনটিকে কল করে তার ফলাফলের ক্লাস-অবজেক্ট তৈরির জন্য ব্যবহার করে উড়ে টাইপ করুন ::

# Instatiate type(form) in order to use the same metaclass as form.
return type(form)(class_name, (form,), form_class_attrs)

এর অর্থ এমনকি কোনও ফর্মের পরিবর্তে একটি curryএড বা partialঅবজেক্ট পাস করার ফলে "হাঁসের আপনাকে কামড় দেয়" এইভাবে কথা বলতে: এটি ModelFormClassকোনও ত্রুটির বার্তাটি ফেরত দিয়ে কোনও অবজেক্টের নির্মাণ পরামিতিগুলির সাথে একটি ফাংশন কল করবে:

function() argument 1 must be code, not str

এটির কাজ করার জন্য আমি একটি জেনারেটর ফাংশন লিখেছিলাম যা প্রথম পরামিতি হিসাবে নির্দিষ্ট কোনও শ্রেণীর সাবক্লাস ফেরত দেওয়ার জন্য ক্লোজার ব্যবহার করে, তারপরে জেনারেটরের ফাংশনের কলগুলিতে সরবরাহিত সংস্থাগুলির সাথে কাওয়ার্গগুলি আইএন করার super.__init__পরে updateকল করে:

def class_gen_with_kwarg(cls, **additionalkwargs):
  """class generator for subclasses with additional 'stored' parameters (in a closure)
     This is required to use a formset_factory with a form that need additional 
     initialization parameters (see http://stackoverflow.com/questions/622982/django-passing-custom-form-parameters-to-formset)
  """
  class ClassWithKwargs(cls):
      def __init__(self, *args, **kwargs):
          kwargs.update(additionalkwargs)
          super(ClassWithKwargs, self).__init__(*args, **kwargs)
  return ClassWithKwargs

তারপরে আপনার কোডটিতে আপনি ফর্ম কারখানাকে কল করবেন:

MyFormSet = inlineformset_factory(ParentModel, Model,form = class_gen_with_kwarg(MyForm, user=self.request.user))

আদেশ সহকারে:

  • এটি কমপক্ষে আপাতত খুব কম পরীক্ষা পেয়েছে
  • সরবরাহকৃত প্যারামিটারগুলি সংঘর্ষে পড়তে পারে এবং যে কোনও কোড ব্যবহারকারীর দ্বারা প্রত্যাবর্তিত অবজেক্টটি ব্যবহার করবে সেগুলি ওভাররাইট করতে পারে

ধন্যবাদ, মনে হচ্ছে জাজানো ১.১০.১ এ এখানে বেশ কয়েকটি অন্যান্য সমাধানের থেকে খুব ভাল কাজ করছে।
fpghost

1
@ এফপিঘোস্ট মনে রাখবেন, কমপক্ষে 1.9 অবধি (আমি এখনও বেশ কয়েকটি কারণে 1.10-তে নেই) যদি আপনার যা করতে হবে তা যদি ফর্মটি তৈরি করা হয় তবে আপনি এটি আপডেট করতে পারেন মাইফর্মসেটটি ব্যবহার করার আগে তার। ক্যোরিসেট বৈশিষ্ট্যটি পরিবর্তন করে ফিরিয়েছে। এই পদ্ধতির চেয়ে কম নমনীয় তবে পড়তে / বুঝতে অনেক সহজ।
রবএম

3

কার্ল মেয়ারের সমাধানটি খুব মার্জিত দেখাচ্ছে। আমি এটি মডেলফর্মেটের জন্য প্রয়োগ করার চেষ্টা করেছি। আমি এই ধারণার মধ্যে ছিলাম যে আমি কোনও শ্রেণীর মধ্যে স্থিতিশীল কল করতে পারি না, তবে নিম্নলিখিত অনভিজ্ঞে কাজ করে:

class MyModel(models.Model):
  myField = models.CharField(max_length=10)

class MyForm(ModelForm):
  _request = None
  class Meta:
    model = MyModel

    def __init__(self,*args,**kwargs):      
      self._request = kwargs.pop('request', None)
      super(MyForm,self).__init__(*args,**kwargs)

class MyFormsetBase(BaseModelFormSet):
  _request = None

def __init__(self,*args,**kwargs):
  self._request = kwargs.pop('request', None)
  subFormClass = self.form
  self.form = curry(subFormClass,request=self._request)
  super(MyFormsetBase,self).__init__(*args,**kwargs)

MyFormset =  modelformset_factory(MyModel,formset=MyFormsetBase,extra=1,max_num=10,can_delete=True)
MyFormset.form = staticmethod(curry(MyForm,request=MyFormsetBase._request))

আমার দৃষ্টিতে, যদি আমি এটির মতো কিছু করি:

formset = MyFormset(request.POST,queryset=MyModel.objects.all(),request=request)

তারপরে "অনুরোধ" কীওয়ার্ডটি আমার ফর্মসেটের সমস্ত সদস্য ফর্মটিতে প্রচারিত হয়। আমি সন্তুষ্ট, তবে কেন এটি কাজ করছে তা আমার কোনও ধারণা নেই - এটি ভুল বলে মনে হচ্ছে। কোনও পরামর্শ?


হুমম ... এখন যদি আমি মাইফর্মসেটের কোনও উদাহরণের ফর্মের বৈশিষ্ট্যটি অ্যাক্সেস করার চেষ্টা করি তবে এটি (সঠিকভাবে) <মাইফর্ম> এর পরিবর্তে <ফাংশন _ক্রিরিড> প্রদান করে। প্রকৃত ফর্মটি কীভাবে অ্যাক্সেস করবেন সে সম্পর্কে কোনও পরামর্শ? আমি চেষ্টা করেছি MyFormSet.form.Meta.model
ট্রুবলিফোন

উফফফফ ... ফর্মটি অ্যাক্সেস করার জন্য আমাকে তরকারী ফাংশনটি কল করতে হবে। MyFormSet.form().Meta.model। প্রকৃতপক্ষে স্পষ্ট।
ট্রুবলিফোন

আমি আপনার সমস্যার সমাধানটি আমার সমস্যার সাথে প্রয়োগ করার চেষ্টা করেছি তবে আমি মনে করি আপনার সম্পূর্ণ উত্তরটি আমি পুরোপুরি বুঝতে পারি না। আপনার মতামত আমার ইস্যু এখানে প্রয়োগ করা যেতে পারে? stackoverflow.com/questions/14176265/...
finspin

1

আমি এই পোস্টিংটি দেখার আগে এই সমস্যাটি সনাক্ত করার চেষ্টা করে কিছু সময় ব্যয় করেছি।

আমি যে সমাধানটি নিয়ে এসেছি তা হ'ল ক্লোজার সলিউশন (এবং এটি এমন একটি সমাধান যা আমি আগে জ্যাঙ্গো মডেল ফর্মগুলির সাথে ব্যবহার করেছি)।

আমি উপরে বর্ণিত হিসাবে কারি () পদ্ধতিটি চেষ্টা করেছি, তবে জাজানো ০.০ এর সাথে কাজ করার জন্য আমি কেবল এটি পেতে পারি নি তাই শেষ পর্যন্ত আমি ক্লোজার পদ্ধতিতে ফিরে এসেছি।

সমাপন পদ্ধতিটি খুব ঝরঝরে এবং একমাত্র সামান্য বিজোড়তা হ'ল শ্রেণি সংজ্ঞাটি ভিউ বা অন্য কোনও ফাংশনের অভ্যন্তরে বাসা বাঁধে। আমার মনে হয় যে এটি আমার কাছে অদ্ভুত লাগছে তা আমার পূর্ববর্তী প্রোগ্রামিং অভিজ্ঞতা থেকে একটি হ্যাঙ্গআপ এবং আমি মনে করি যে আরও গতিশীল ভাষাগুলির ব্যাকগ্রাউন্ডধারী কেউ একটি চোখের পাতাটি ব্যাট করবেন না!


1

আমাকেও একই কাজ করতে হয়েছিল। এটি curryসমাধানের অনুরূপ :

def form_with_my_variable(myvar):
   class MyForm(ServiceForm):
     def __init__(self, myvar=myvar, *args, **kwargs):
       super(SeriveForm, self).__init__(myvar=myvar, *args, **kwargs)
   return MyForm

factory = inlineformset_factory(..., form=form_with_my_variable(myvar), ... )

1

এই উত্তরের ভিত্তিতে আমি আরও স্পষ্ট সমাধান পেয়েছি:

class ServiceForm(forms.Form):
    option = forms.ModelChoiceField(
            queryset=ServiceOption.objects.filter(affiliate=self.affiliate))
    rate = forms.DecimalField(widget=custom_widgets.SmallField())
    units = forms.IntegerField(min_value=1, 
            widget=custom_widgets.SmallField())

    @staticmethod
    def make_service_form(affiliate):
        self.affiliate = affiliate
        return ServiceForm

এবং মত দেখুন চালানো

formset_factory(form=ServiceForm.make_service_form(affiliate))

6
জ্যাঙ্গো 1.9 এর পরিবর্তে form_kwargs ব্যবহার করে এই যে কোনও অপ্রয়োজনীয় তৈরি করেছে।
পাওলো বার্গান্টিনো

আমার বর্তমান কাজে আমাদের লিগ্যাসি
জাঙ্গো

0

আমি এখানে নবাগত তাই আমি মন্তব্য যুক্ত করতে পারি না। আমি আশা করি এই কোডটিও কার্যকর হবে:

ServiceFormSet = formset_factory(ServiceForm, extra=3)

ServiceFormSet.formset = staticmethod(curry(ServiceForm, affiliate=request.affiliate))

BaseFormSetফর্মের পরিবর্তে ফর্মসেটে অতিরিক্ত পরামিতি যুক্ত করার জন্য ।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.